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 17package com.android.cellbroadcastreceiver; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.SharedPreferences; 23import android.content.SharedPreferences.Editor; 24import android.os.UserManager; 25import android.preference.PreferenceManager; 26import android.provider.Telephony; 27import android.telephony.ServiceState; 28import android.telephony.cdma.CdmaSmsCbProgramData; 29import android.util.Log; 30 31import com.android.internal.telephony.IccCardConstants; 32import com.android.internal.telephony.TelephonyIntents; 33import com.android.internal.telephony.cdma.sms.SmsEnvelope; 34 35public class CellBroadcastReceiver extends BroadcastReceiver { 36 private static final String TAG = "CellBroadcastReceiver"; 37 static final boolean DBG = false; // STOPSHIP: change to false before ship 38 private static int mServiceState = -1; 39 40 public static final String CELLBROADCAST_START_CONFIG_ACTION = 41 "android.cellbroadcastreceiver.START_CONFIG"; 42 43 // Key to access the stored reminder interval default value 44 private static final String CURRENT_INTERVAL_DEFAULT = "current_interval_default"; 45 46 @Override 47 public void onReceive(Context context, Intent intent) { 48 onReceiveWithPrivilege(context, intent, false); 49 } 50 51 protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { 52 if (DBG) log("onReceive " + intent); 53 54 String action = intent.getAction(); 55 56 if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) { 57 if (DBG) log("Intent: " + action); 58 ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras()); 59 if (serviceState != null) { 60 int newState = serviceState.getState(); 61 if (newState != mServiceState) { 62 Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState + 63 " Current state=" + mServiceState); 64 mServiceState = newState; 65 if (((newState == ServiceState.STATE_IN_SERVICE) || 66 (newState == ServiceState.STATE_EMERGENCY_ONLY)) && 67 (UserManager.get(context).isSystemUser())) { 68 startConfigService(context.getApplicationContext()); 69 } 70 } 71 } 72 } else if (TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_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 else { 81 Log.e(TAG, "Not system user. Ignored the intent " + action); 82 } 83 } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || 84 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { 85 // If 'privileged' is false, it means that the intent was delivered to the base 86 // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it 87 // means someone has tried to spoof the message by delivering it outside the normal 88 // permission-checked route, so we just ignore it. 89 if (privileged) { 90 intent.setClass(context, CellBroadcastAlertService.class); 91 context.startService(intent); 92 } else { 93 loge("ignoring unprivileged action received " + action); 94 } 95 } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION 96 .equals(action)) { 97 if (privileged) { 98 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[]) 99 intent.getParcelableArrayExtra("program_data_list"); 100 if (programDataList != null) { 101 handleCdmaSmsCbProgramData(context, programDataList); 102 } else { 103 loge("SCPD intent received with no program_data_list"); 104 } 105 } else { 106 loge("ignoring unprivileged action received " + action); 107 } 108 } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { 109 String simState = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 110 // Whenever sim is loaded, we need to adjust the emergency alert 111 // reminder interval list because it might change since different 112 // countries/carriers might have different interval settings. 113 if (simState.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) { 114 adjustReminderInterval(context.getApplicationContext()); 115 } 116 } else { 117 Log.w(TAG, "onReceive() unexpected action " + action); 118 } 119 } 120 121 private void adjustReminderInterval(Context context) { 122 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 */ 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 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 */ 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 224 private static void log(String msg) { 225 Log.d(TAG, msg); 226 } 227 228 private static void loge(String msg) { 229 Log.e(TAG, msg); 230 } 231} 232