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.telephony.ServiceState; 28 import android.telephony.cdma.CdmaSmsCbProgramData; 29 import android.util.Log; 30 31 import com.android.internal.telephony.IccCardConstants; 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 private static int mServiceState = -1; 40 41 public static final String CELLBROADCAST_START_CONFIG_ACTION = 42 "android.cellbroadcastreceiver.START_CONFIG"; 43 44 // Key to access the stored reminder interval default value 45 private static final String CURRENT_INTERVAL_DEFAULT = "current_interval_default"; 46 47 @Override onReceive(Context context, Intent intent)48 public void onReceive(Context context, Intent intent) { 49 onReceiveWithPrivilege(context, intent, false); 50 } 51 onReceiveWithPrivilege(Context context, Intent intent, boolean privileged)52 protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { 53 if (DBG) log("onReceive " + intent); 54 55 String action = intent.getAction(); 56 57 if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) { 58 if (DBG) log("Intent: " + action); 59 ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras()); 60 if (serviceState != null) { 61 int newState = serviceState.getState(); 62 if (newState != mServiceState) { 63 Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState + 64 " Current state=" + mServiceState); 65 mServiceState = newState; 66 if (((newState == ServiceState.STATE_IN_SERVICE) || 67 (newState == ServiceState.STATE_EMERGENCY_ONLY)) && 68 (UserManager.get(context).isSystemUser())) { 69 startConfigService(context.getApplicationContext()); 70 } 71 } 72 } 73 } else if (TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.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 else { 82 Log.e(TAG, "Not system user. Ignored the intent " + action); 83 } 84 } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || 85 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { 86 // If 'privileged' is false, it means that the intent was delivered to the base 87 // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it 88 // means someone has tried to spoof the message by delivering it outside the normal 89 // permission-checked route, so we just ignore it. 90 if (privileged) { 91 intent.setClass(context, CellBroadcastAlertService.class); 92 context.startService(intent); 93 } else { 94 loge("ignoring unprivileged action received " + action); 95 } 96 } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION 97 .equals(action)) { 98 if (privileged) { 99 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[]) 100 intent.getParcelableArrayExtra("program_data_list"); 101 if (programDataList != null) { 102 handleCdmaSmsCbProgramData(context, programDataList); 103 } else { 104 loge("SCPD intent received with no program_data_list"); 105 } 106 } else { 107 loge("ignoring unprivileged action received " + action); 108 } 109 } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { 110 String simState = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 111 // Whenever sim is loaded, we need to adjust the emergency alert 112 // reminder interval list because it might change since different 113 // countries/carriers might have different interval settings. 114 if (simState.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) { 115 adjustReminderInterval(context.getApplicationContext()); 116 } 117 } else { 118 Log.w(TAG, "onReceive() unexpected action " + action); 119 } 120 } 121 adjustReminderInterval(Context context)122 private void adjustReminderInterval(Context context) { 123 124 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); 125 String currentIntervalDefault = sp.getString(CURRENT_INTERVAL_DEFAULT, "0"); 126 127 // If interval default changes, reset the interval to the new default value. 128 String newIntervalDefault = context.getResources().getString( 129 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 146 /** 147 * Handle Service Category Program Data message. 148 * TODO: Send Service Category Program Results response message to sender 149 * 150 * @param context 151 * @param programDataList 152 */ handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)153 private void handleCdmaSmsCbProgramData(Context context, 154 CdmaSmsCbProgramData[] programDataList) { 155 for (CdmaSmsCbProgramData programData : programDataList) { 156 switch (programData.getOperation()) { 157 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: 158 tryCdmaSetCategory(context, programData.getCategory(), true); 159 break; 160 161 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: 162 tryCdmaSetCategory(context, programData.getCategory(), false); 163 break; 164 165 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: 166 tryCdmaSetCategory(context, 167 SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); 168 tryCdmaSetCategory(context, 169 SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); 170 tryCdmaSetCategory(context, 171 SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); 172 tryCdmaSetCategory(context, 173 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); 174 break; 175 176 default: 177 loge("Ignoring unknown SCPD operation " + programData.getOperation()); 178 } 179 } 180 } 181 tryCdmaSetCategory(Context context, int category, boolean enable)182 private void tryCdmaSetCategory(Context context, int category, boolean enable) { 183 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 184 185 switch (category) { 186 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 187 sharedPrefs.edit().putBoolean( 188 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable) 189 .apply(); 190 break; 191 192 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 193 sharedPrefs.edit().putBoolean( 194 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable) 195 .apply(); 196 break; 197 198 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 199 sharedPrefs.edit().putBoolean( 200 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply(); 201 break; 202 203 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 204 sharedPrefs.edit().putBoolean( 205 CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply(); 206 break; 207 208 default: 209 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable") 210 + " alerts in category " + category); 211 } 212 } 213 214 /** 215 * Tell {@link CellBroadcastConfigService} to enable the CB channels. 216 * @param context the broadcast receiver context 217 */ startConfigService(Context context)218 static void startConfigService(Context context) { 219 Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 220 null, context, CellBroadcastConfigService.class); 221 Log.d(TAG, "Start Cell Broadcast configuration."); 222 context.startService(serviceIntent); 223 } 224 log(String msg)225 private static void log(String msg) { 226 Log.d(TAG, msg); 227 } 228 loge(String msg)229 private static void loge(String msg) { 230 Log.e(TAG, msg); 231 } 232 } 233