1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cellbroadcastreceiver; 18 19 import android.app.ActivityManager; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.SharedPreferences; 24 import android.content.SharedPreferences.Editor; 25 import android.os.Bundle; 26 import android.os.UserManager; 27 import android.preference.PreferenceManager; 28 import android.provider.Telephony; 29 import android.provider.Telephony.CellBroadcasts; 30 import android.telephony.CarrierConfigManager; 31 import android.telephony.ServiceState; 32 import android.telephony.SubscriptionManager; 33 import android.telephony.cdma.CdmaSmsCbProgramData; 34 import android.util.Log; 35 36 import com.android.internal.telephony.cdma.sms.SmsEnvelope; 37 38 public class CellBroadcastReceiver extends BroadcastReceiver { 39 private static final String TAG = "CellBroadcastReceiver"; 40 static final boolean DBG = true; 41 static final boolean VDBG = false; // STOPSHIP: change to false before ship 42 43 // Key to access the stored reminder interval default value 44 private static final String CURRENT_INTERVAL_DEFAULT = "current_interval_default"; 45 46 // Intent actions and extras 47 public static final String CELLBROADCAST_START_CONFIG_ACTION = 48 "com.android.cellbroadcastreceiver.intent.START_CONFIG"; 49 public static final String ACTION_MARK_AS_READ = 50 "com.android.cellbroadcastreceiver.intent.action.MARK_AS_READ"; 51 public static final String EXTRA_DELIVERY_TIME = 52 "com.android.cellbroadcastreceiver.intent.extra.ID"; 53 54 @Override onReceive(Context context, Intent intent)55 public void onReceive(Context context, Intent intent) { 56 onReceiveWithPrivilege(context, intent, false); 57 } 58 onReceiveWithPrivilege(Context context, Intent intent, boolean privileged)59 protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { 60 if (DBG) log("onReceive " + intent); 61 62 String action = intent.getAction(); 63 64 if (ACTION_MARK_AS_READ.equals(action)) { 65 final long deliveryTime = intent.getLongExtra(EXTRA_DELIVERY_TIME, -1); 66 new CellBroadcastContentProvider.AsyncCellBroadcastTask(context.getContentResolver()) 67 .execute(new CellBroadcastContentProvider.CellBroadcastOperation() { 68 @Override 69 public boolean execute(CellBroadcastContentProvider provider) { 70 return provider.markBroadcastRead(CellBroadcasts.DELIVERY_TIME, 71 deliveryTime); 72 } 73 }); 74 } else if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { 75 initializeSharedPreference(context.getApplicationContext()); 76 startConfigService(context.getApplicationContext()); 77 } else if (CELLBROADCAST_START_CONFIG_ACTION.equals(action) 78 || SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.equals(action)) { 79 startConfigService(context.getApplicationContext()); 80 } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || 81 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { 82 // If 'privileged' is false, it means that the intent was delivered to the base 83 // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it 84 // means someone has tried to spoof the message by delivering it outside the normal 85 // permission-checked route, so we just ignore it. 86 if (privileged) { 87 intent.setClass(context, CellBroadcastAlertService.class); 88 context.startService(intent); 89 } else { 90 loge("ignoring unprivileged action received " + action); 91 } 92 } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION 93 .equals(action)) { 94 if (privileged) { 95 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[]) 96 intent.getParcelableArrayExtra("program_data_list"); 97 if (programDataList != null) { 98 handleCdmaSmsCbProgramData(context, programDataList); 99 } else { 100 loge("SCPD intent received with no program_data_list"); 101 } 102 } else { 103 loge("ignoring unprivileged action received " + action); 104 } 105 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { 106 // rename registered notification channels on locale change 107 CellBroadcastAlertService.createNotificationChannels(context); 108 } else if (Intent.ACTION_SERVICE_STATE.equals(action)) { 109 if (CellBroadcastSettings.getResourcesForDefaultSmsSubscriptionId(context).getBoolean( 110 R.bool.reset_duplicate_detection_on_airplane_mode)) { 111 Bundle extras = intent.getExtras(); 112 ServiceState ss = ServiceState.newFromBundle(extras); 113 if (ss.getState() == ServiceState.STATE_POWER_OFF) { 114 CellBroadcastAlertService.resetMessageDuplicateDetection(); 115 } 116 } 117 } else { 118 Log.w(TAG, "onReceive() unexpected action " + action); 119 } 120 } 121 adjustReminderInterval(Context context)122 private void adjustReminderInterval(Context context) { 123 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); 124 String currentIntervalDefault = sp.getString(CURRENT_INTERVAL_DEFAULT, "0"); 125 126 // If interval default changes, reset the interval to the new default value. 127 String newIntervalDefault = 128 CellBroadcastSettings.getResourcesForDefaultSmsSubscriptionId(context) 129 .getString(R.string.alert_reminder_interval_default_value); 130 if (!newIntervalDefault.equals(currentIntervalDefault)) { 131 Log.d(TAG, "Default interval changed from " + currentIntervalDefault + " to " + 132 newIntervalDefault); 133 134 Editor editor = sp.edit(); 135 // Reset the value to default. 136 editor.putString( 137 CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, newIntervalDefault); 138 // Save the new default value. 139 editor.putString(CURRENT_INTERVAL_DEFAULT, newIntervalDefault); 140 editor.commit(); 141 } else { 142 if (DBG) Log.d(TAG, "Default interval " + currentIntervalDefault + " did not change."); 143 } 144 } 145 initializeSharedPreference(Context context)146 private void initializeSharedPreference(Context context) { 147 if (UserManager.get(context).isSystemUser()) { 148 Log.d(TAG, "initializeSharedPreference"); 149 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); 150 if (!sp.getBoolean(PreferenceManager.KEY_HAS_SET_DEFAULT_VALUES, false)) { 151 // Sets the default values of the shared preference if there isn't any. 152 PreferenceManager.setDefaultValues(context, R.xml.preferences, false); 153 154 // If the device is in test harness mode, we need to disable emergency alert by 155 // default. 156 if (ActivityManager.isRunningInUserTestHarness()) { 157 Log.d(TAG, "In test harness mode. Turn off emergency alert by default."); 158 sp.edit().putBoolean(CellBroadcastSettings.KEY_ENABLE_ALERTS_MASTER_TOGGLE, 159 false).apply(); 160 } 161 } else { 162 Log.d(TAG, "Skip setting default values of shared preference."); 163 } 164 165 adjustReminderInterval(context); 166 } else { 167 Log.e(TAG, "initializeSharedPreference: Not system user."); 168 } 169 } 170 171 /** 172 * Handle Service Category Program Data message. 173 * TODO: Send Service Category Program Results response message to sender 174 * 175 * @param context 176 * @param programDataList 177 */ handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)178 private void handleCdmaSmsCbProgramData(Context context, 179 CdmaSmsCbProgramData[] programDataList) { 180 for (CdmaSmsCbProgramData programData : programDataList) { 181 switch (programData.getOperation()) { 182 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: 183 tryCdmaSetCategory(context, programData.getCategory(), true); 184 break; 185 186 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: 187 tryCdmaSetCategory(context, programData.getCategory(), false); 188 break; 189 190 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: 191 tryCdmaSetCategory(context, 192 SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); 193 tryCdmaSetCategory(context, 194 SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); 195 tryCdmaSetCategory(context, 196 SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); 197 tryCdmaSetCategory(context, 198 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); 199 break; 200 201 default: 202 loge("Ignoring unknown SCPD operation " + programData.getOperation()); 203 } 204 } 205 } 206 tryCdmaSetCategory(Context context, int category, boolean enable)207 private void tryCdmaSetCategory(Context context, int category, boolean enable) { 208 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 209 210 switch (category) { 211 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 212 sharedPrefs.edit().putBoolean( 213 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable) 214 .apply(); 215 break; 216 217 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 218 sharedPrefs.edit().putBoolean( 219 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable) 220 .apply(); 221 break; 222 223 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 224 sharedPrefs.edit().putBoolean( 225 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply(); 226 break; 227 228 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 229 sharedPrefs.edit().putBoolean( 230 CellBroadcastSettings.KEY_ENABLE_TEST_ALERTS, enable).apply(); 231 break; 232 233 default: 234 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable") 235 + " alerts in category " + category); 236 } 237 } 238 239 /** 240 * Tell {@link CellBroadcastConfigService} to enable the CB channels. 241 * @param context the broadcast receiver context 242 */ startConfigService(Context context)243 static void startConfigService(Context context) { 244 if (UserManager.get(context).isSystemUser()) { 245 Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 246 null, context, CellBroadcastConfigService.class); 247 Log.d(TAG, "Start Cell Broadcast configuration."); 248 context.startService(serviceIntent); 249 } else { 250 Log.e(TAG, "startConfigService: Not system user."); 251 } 252 } 253 log(String msg)254 private static void log(String msg) { 255 Log.d(TAG, msg); 256 } 257 loge(String msg)258 private static void loge(String msg) { 259 Log.e(TAG, msg); 260 } 261 } 262