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.content.res.Configuration; 26 import android.os.RemoteException; 27 import android.os.UserManager; 28 import android.preference.PreferenceManager; 29 import android.provider.Telephony; 30 import android.provider.Telephony.CellBroadcasts; 31 import android.telephony.CarrierConfigManager; 32 import android.telephony.cdma.CdmaSmsCbProgramData; 33 import android.util.Log; 34 35 import com.android.internal.telephony.TelephonyIntents; 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 (TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.equals(action) 75 || CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) 76 || Intent.ACTION_BOOT_COMPLETED.equals(action) 77 || CELLBROADCAST_START_CONFIG_ACTION.equals(action)) { 78 // Set default values for preferences. 79 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { 80 try { 81 Configuration configuration = ActivityManager.getService().getConfiguration(); 82 if (configuration.mcc != 0 && configuration.mnc != 0) { 83 PreferenceManager.setDefaultValues(context, R.xml.preferences, false); 84 } 85 } catch (RemoteException e) { 86 } 87 } 88 // Todo: Add the service state check once the new get service state API is done. 89 // Do not rely on mServiceState as it gets reset to -1 time to time because 90 // the process of CellBroadcastReceiver gets killed every time once the job is done. 91 if (UserManager.get(context).isSystemUser()) { 92 startConfigService(context.getApplicationContext()); 93 94 // Whenever carrier changes, we need to adjust the emergency alert 95 // reminder interval list because it might change since different 96 // countries/carriers might have different interval settings. 97 adjustReminderInterval(context); 98 } 99 else { 100 Log.e(TAG, "Not system user. Ignored the intent " + action); 101 } 102 } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || 103 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { 104 // If 'privileged' is false, it means that the intent was delivered to the base 105 // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it 106 // means someone has tried to spoof the message by delivering it outside the normal 107 // permission-checked route, so we just ignore it. 108 if (privileged) { 109 intent.setClass(context, CellBroadcastAlertService.class); 110 context.startService(intent); 111 } else { 112 loge("ignoring unprivileged action received " + action); 113 } 114 } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION 115 .equals(action)) { 116 if (privileged) { 117 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[]) 118 intent.getParcelableArrayExtra("program_data_list"); 119 if (programDataList != null) { 120 handleCdmaSmsCbProgramData(context, programDataList); 121 } else { 122 loge("SCPD intent received with no program_data_list"); 123 } 124 } else { 125 loge("ignoring unprivileged action received " + action); 126 } 127 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { 128 // rename registered notification channels on locale change 129 CellBroadcastAlertService.createNotificationChannels(context); 130 } else { 131 Log.w(TAG, "onReceive() unexpected action " + action); 132 } 133 } 134 adjustReminderInterval(Context context)135 private void adjustReminderInterval(Context context) { 136 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); 137 String currentIntervalDefault = sp.getString(CURRENT_INTERVAL_DEFAULT, "0"); 138 139 // If interval default changes, reset the interval to the new default value. 140 String newIntervalDefault = context.getResources().getString( 141 R.string.alert_reminder_interval_default_value); 142 if (!newIntervalDefault.equals(currentIntervalDefault)) { 143 Log.d(TAG, "Default interval changed from " + currentIntervalDefault + " to " + 144 newIntervalDefault); 145 146 Editor editor = sp.edit(); 147 // Reset the value to default. 148 editor.putString( 149 CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, newIntervalDefault); 150 // Save the new default value. 151 editor.putString(CURRENT_INTERVAL_DEFAULT, newIntervalDefault); 152 editor.commit(); 153 } else { 154 if (DBG) Log.d(TAG, "Default interval " + currentIntervalDefault + " did not change."); 155 } 156 } 157 158 /** 159 * Handle Service Category Program Data message. 160 * TODO: Send Service Category Program Results response message to sender 161 * 162 * @param context 163 * @param programDataList 164 */ handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)165 private void handleCdmaSmsCbProgramData(Context context, 166 CdmaSmsCbProgramData[] programDataList) { 167 for (CdmaSmsCbProgramData programData : programDataList) { 168 switch (programData.getOperation()) { 169 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: 170 tryCdmaSetCategory(context, programData.getCategory(), true); 171 break; 172 173 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: 174 tryCdmaSetCategory(context, programData.getCategory(), false); 175 break; 176 177 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: 178 tryCdmaSetCategory(context, 179 SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); 180 tryCdmaSetCategory(context, 181 SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); 182 tryCdmaSetCategory(context, 183 SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); 184 tryCdmaSetCategory(context, 185 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); 186 break; 187 188 default: 189 loge("Ignoring unknown SCPD operation " + programData.getOperation()); 190 } 191 } 192 } 193 tryCdmaSetCategory(Context context, int category, boolean enable)194 private void tryCdmaSetCategory(Context context, int category, boolean enable) { 195 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 196 197 switch (category) { 198 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 199 sharedPrefs.edit().putBoolean( 200 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable) 201 .apply(); 202 break; 203 204 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 205 sharedPrefs.edit().putBoolean( 206 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable) 207 .apply(); 208 break; 209 210 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 211 sharedPrefs.edit().putBoolean( 212 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply(); 213 break; 214 215 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 216 sharedPrefs.edit().putBoolean( 217 CellBroadcastSettings.KEY_ENABLE_TEST_ALERTS, enable).apply(); 218 break; 219 220 default: 221 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable") 222 + " alerts in category " + category); 223 } 224 } 225 226 /** 227 * Tell {@link CellBroadcastConfigService} to enable the CB channels. 228 * @param context the broadcast receiver context 229 */ startConfigService(Context context)230 static void startConfigService(Context context) { 231 Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 232 null, context, CellBroadcastConfigService.class); 233 Log.d(TAG, "Start Cell Broadcast configuration."); 234 context.startService(serviceIntent); 235 } 236 log(String msg)237 private static void log(String msg) { 238 Log.d(TAG, msg); 239 } 240 loge(String msg)241 private static void loge(String msg) { 242 Log.e(TAG, msg); 243 } 244 } 245