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