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.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.SharedPreferences; 23 import android.content.SharedPreferences.Editor; 24 import android.os.UserManager; 25 import android.preference.PreferenceManager; 26 import android.provider.Telephony; 27 import android.provider.Telephony.CellBroadcasts; 28 import android.telephony.CarrierConfigManager; 29 import android.telephony.cdma.CdmaSmsCbProgramData; 30 import android.util.Log; 31 32 import com.android.internal.telephony.TelephonyIntents; 33 import com.android.internal.telephony.cdma.sms.SmsEnvelope; 34 35 public class CellBroadcastReceiver extends BroadcastReceiver { 36 private static final String TAG = "CellBroadcastReceiver"; 37 static final boolean DBG = true; 38 static final boolean VDBG = false; // STOPSHIP: change to false before ship 39 40 // Key to access the stored reminder interval default value 41 private static final String CURRENT_INTERVAL_DEFAULT = "current_interval_default"; 42 43 // Intent actions and extras 44 public static final String CELLBROADCAST_START_CONFIG_ACTION = 45 "com.android.cellbroadcastreceiver.intent.START_CONFIG"; 46 public static final String ACTION_MARK_AS_READ = 47 "com.android.cellbroadcastreceiver.intent.action.MARK_AS_READ"; 48 public static final String EXTRA_DELIVERY_TIME = 49 "com.android.cellbroadcastreceiver.intent.extra.ID"; 50 51 @Override onReceive(Context context, Intent intent)52 public void onReceive(Context context, Intent intent) { 53 onReceiveWithPrivilege(context, intent, false); 54 } 55 onReceiveWithPrivilege(Context context, Intent intent, boolean privileged)56 protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { 57 if (DBG) log("onReceive " + intent); 58 59 String action = intent.getAction(); 60 61 if (ACTION_MARK_AS_READ.equals(action)) { 62 final long deliveryTime = intent.getLongExtra(EXTRA_DELIVERY_TIME, -1); 63 new CellBroadcastContentProvider.AsyncCellBroadcastTask(context.getContentResolver()) 64 .execute(new CellBroadcastContentProvider.CellBroadcastOperation() { 65 @Override 66 public boolean execute(CellBroadcastContentProvider provider) { 67 return provider.markBroadcastRead(CellBroadcasts.DELIVERY_TIME, 68 deliveryTime); 69 } 70 }); 71 } else if (TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.equals(action) 72 || CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) 73 || Intent.ACTION_BOOT_COMPLETED.equals(action) 74 || CELLBROADCAST_START_CONFIG_ACTION.equals(action)) { 75 // Todo: Add the service state check once the new get service state API is done. 76 // Do not rely on mServiceState as it gets reset to -1 time to time because 77 // the process of CellBroadcastReceiver gets killed every time once the job is done. 78 if (UserManager.get(context).isSystemUser()) { 79 startConfigService(context.getApplicationContext()); 80 81 // Whenever carrier changes, we need to adjust the emergency alert 82 // reminder interval list because it might change since different 83 // countries/carriers might have different interval settings. 84 adjustReminderInterval(context); 85 } 86 else { 87 Log.e(TAG, "Not system user. Ignored the intent " + action); 88 } 89 } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || 90 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { 91 // If 'privileged' is false, it means that the intent was delivered to the base 92 // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it 93 // means someone has tried to spoof the message by delivering it outside the normal 94 // permission-checked route, so we just ignore it. 95 if (privileged) { 96 intent.setClass(context, CellBroadcastAlertService.class); 97 context.startService(intent); 98 } else { 99 loge("ignoring unprivileged action received " + action); 100 } 101 } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION 102 .equals(action)) { 103 if (privileged) { 104 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[]) 105 intent.getParcelableArrayExtra("program_data_list"); 106 if (programDataList != null) { 107 handleCdmaSmsCbProgramData(context, programDataList); 108 } else { 109 loge("SCPD intent received with no program_data_list"); 110 } 111 } else { 112 loge("ignoring unprivileged action received " + action); 113 } 114 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { 115 // rename registered notification channels on locale change 116 CellBroadcastAlertService.createNotificationChannels(context); 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 = context.getResources().getString( 128 R.string.alert_reminder_interval_default_value); 129 if (!newIntervalDefault.equals(currentIntervalDefault)) { 130 Log.d(TAG, "Default interval changed from " + currentIntervalDefault + " to " + 131 newIntervalDefault); 132 133 Editor editor = sp.edit(); 134 // Reset the value to default. 135 editor.putString( 136 CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, newIntervalDefault); 137 // Save the new default value. 138 editor.putString(CURRENT_INTERVAL_DEFAULT, newIntervalDefault); 139 editor.commit(); 140 } else { 141 if (DBG) Log.d(TAG, "Default interval " + currentIntervalDefault + " did not change."); 142 } 143 } 144 145 /** 146 * Handle Service Category Program Data message. 147 * TODO: Send Service Category Program Results response message to sender 148 * 149 * @param context 150 * @param programDataList 151 */ handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)152 private void handleCdmaSmsCbProgramData(Context context, 153 CdmaSmsCbProgramData[] programDataList) { 154 for (CdmaSmsCbProgramData programData : programDataList) { 155 switch (programData.getOperation()) { 156 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: 157 tryCdmaSetCategory(context, programData.getCategory(), true); 158 break; 159 160 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: 161 tryCdmaSetCategory(context, programData.getCategory(), false); 162 break; 163 164 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: 165 tryCdmaSetCategory(context, 166 SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); 167 tryCdmaSetCategory(context, 168 SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); 169 tryCdmaSetCategory(context, 170 SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); 171 tryCdmaSetCategory(context, 172 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); 173 break; 174 175 default: 176 loge("Ignoring unknown SCPD operation " + programData.getOperation()); 177 } 178 } 179 } 180 tryCdmaSetCategory(Context context, int category, boolean enable)181 private void tryCdmaSetCategory(Context context, int category, boolean enable) { 182 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 183 184 switch (category) { 185 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 186 sharedPrefs.edit().putBoolean( 187 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable) 188 .apply(); 189 break; 190 191 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 192 sharedPrefs.edit().putBoolean( 193 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable) 194 .apply(); 195 break; 196 197 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 198 sharedPrefs.edit().putBoolean( 199 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply(); 200 break; 201 202 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 203 sharedPrefs.edit().putBoolean( 204 CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply(); 205 break; 206 207 default: 208 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable") 209 + " alerts in category " + category); 210 } 211 } 212 213 /** 214 * Tell {@link CellBroadcastConfigService} to enable the CB channels. 215 * @param context the broadcast receiver context 216 */ startConfigService(Context context)217 static void startConfigService(Context context) { 218 Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 219 null, context, CellBroadcastConfigService.class); 220 Log.d(TAG, "Start Cell Broadcast configuration."); 221 context.startService(serviceIntent); 222 } 223 log(String msg)224 private static void log(String msg) { 225 Log.d(TAG, msg); 226 } 227 loge(String msg)228 private static void loge(String msg) { 229 Log.e(TAG, msg); 230 } 231 } 232