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.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 import android.preference.PreferenceManager; 28 import android.provider.Telephony; 29 import android.telephony.CellBroadcastMessage; 30 import android.telephony.ServiceState; 31 import android.telephony.SubscriptionManager; 32 import android.telephony.TelephonyManager; 33 import android.telephony.cdma.CdmaSmsCbProgramData; 34 import android.util.Log; 35 36 import com.android.internal.telephony.cdma.sms.SmsEnvelope; 37 import com.android.internal.telephony.TelephonyIntents; 38 39 public class CellBroadcastReceiver extends BroadcastReceiver { 40 private static final String TAG = "CellBroadcastReceiver"; 41 static final boolean DBG = false; // STOPSHIP: change to false before ship 42 private static int mServiceState = -1; 43 44 public static final String CELLBROADCAST_START_CONFIG_ACTION = 45 "android.cellbroadcastreceiver.START_CONFIG"; 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 { 110 Log.w(TAG, "onReceive() unexpected action " + action); 111 } 112 } 113 114 /** 115 * Handle Service Category Program Data message. 116 * TODO: Send Service Category Program Results response message to sender 117 * 118 * @param context 119 * @param programDataList 120 */ handleCdmaSmsCbProgramData(Context context, CdmaSmsCbProgramData[] programDataList)121 private void handleCdmaSmsCbProgramData(Context context, 122 CdmaSmsCbProgramData[] programDataList) { 123 for (CdmaSmsCbProgramData programData : programDataList) { 124 switch (programData.getOperation()) { 125 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: 126 tryCdmaSetCategory(context, programData.getCategory(), true); 127 break; 128 129 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: 130 tryCdmaSetCategory(context, programData.getCategory(), false); 131 break; 132 133 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: 134 tryCdmaSetCategory(context, 135 SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); 136 tryCdmaSetCategory(context, 137 SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); 138 tryCdmaSetCategory(context, 139 SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); 140 tryCdmaSetCategory(context, 141 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); 142 break; 143 144 default: 145 loge("Ignoring unknown SCPD operation " + programData.getOperation()); 146 } 147 } 148 } 149 tryCdmaSetCategory(Context context, int category, boolean enable)150 private void tryCdmaSetCategory(Context context, int category, boolean enable) { 151 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 152 153 switch (category) { 154 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 155 sharedPrefs.edit().putBoolean( 156 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable) 157 .apply(); 158 break; 159 160 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 161 sharedPrefs.edit().putBoolean( 162 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable) 163 .apply(); 164 break; 165 166 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 167 sharedPrefs.edit().putBoolean( 168 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply(); 169 break; 170 171 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 172 sharedPrefs.edit().putBoolean( 173 CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply(); 174 break; 175 176 default: 177 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable") 178 + " alerts in category " + category); 179 } 180 } 181 182 /** 183 * Tell {@link CellBroadcastConfigService} to enable the CB channels. 184 * @param context the broadcast receiver context 185 */ startConfigService(Context context)186 static void startConfigService(Context context) { 187 Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 188 null, context, CellBroadcastConfigService.class); 189 Log.d(TAG, "Start Cell Broadcast configuration."); 190 context.startService(serviceIntent); 191 } 192 193 /** 194 * @return true if the phone is a CDMA phone type 195 */ phoneIsCdma()196 static boolean phoneIsCdma() { 197 boolean isCdma = false; 198 199 int subId = SubscriptionManager.getDefaultSmsSubscriptionId(); 200 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 201 subId = SubscriptionManager.getDefaultSubscriptionId(); 202 } 203 204 TelephonyManager tm = TelephonyManager.getDefault(); 205 if (tm != null) { 206 isCdma = (tm.getCurrentPhoneType(subId) == TelephonyManager.PHONE_TYPE_CDMA); 207 } 208 return isCdma; 209 } 210 log(String msg)211 private static void log(String msg) { 212 Log.d(TAG, msg); 213 } 214 loge(String msg)215 private static void loge(String msg) { 216 Log.e(TAG, msg); 217 } 218 } 219