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.pm.PackageManager; 24 import android.os.RemoteException; 25 import android.os.ServiceManager; 26 import android.os.UserHandle; 27 import android.preference.PreferenceManager; 28 import android.provider.Telephony; 29 import android.telephony.CellBroadcastMessage; 30 import android.telephony.ServiceState; 31 import android.telephony.TelephonyManager; 32 import android.telephony.cdma.CdmaSmsCbProgramData; 33 import android.util.Log; 34 35 import com.android.internal.telephony.ITelephony; 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 = true; // STOPSHIP: change to false before ship 42 private int mServiceState = -1; 43 private static final String GET_LATEST_CB_AREA_INFO_ACTION = 44 "android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO"; 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_SERVICE_STATE_CHANGED"); 58 ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras()); 59 int newState = serviceState.getState(); 60 if (newState != mServiceState) { 61 Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState + 62 " Current state=" + mServiceState); 63 mServiceState = newState; 64 if (newState == ServiceState.STATE_IN_SERVICE || 65 newState == ServiceState.STATE_EMERGENCY_ONLY) { 66 startConfigService(context.getApplicationContext()); 67 } 68 } 69 } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { 70 boolean airplaneModeOn = intent.getBooleanExtra("state", false); 71 if (DBG) log("airplaneModeOn: " + airplaneModeOn); 72 if (!airplaneModeOn) { 73 startConfigService(context); 74 } 75 } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || 76 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { 77 // If 'privileged' is false, it means that the intent was delivered to the base 78 // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it 79 // means someone has tried to spoof the message by delivering it outside the normal 80 // permission-checked route, so we just ignore it. 81 if (privileged) { 82 intent.setClass(context, CellBroadcastAlertService.class); 83 context.startService(intent); 84 } else { 85 loge("ignoring unprivileged action received " + action); 86 } 87 } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION 88 .equals(action)) { 89 if (privileged) { 90 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[]) 91 intent.getParcelableArrayExtra("program_data_list"); 92 if (programDataList != null) { 93 handleCdmaSmsCbProgramData(context, programDataList); 94 } else { 95 loge("SCPD intent received with no program_data_list"); 96 } 97 } else { 98 loge("ignoring unprivileged action received " + action); 99 } 100 } else if (GET_LATEST_CB_AREA_INFO_ACTION.equals(action)) { 101 if (privileged) { 102 CellBroadcastMessage message = CellBroadcastReceiverApp.getLatestAreaInfo(); 103 if (message != null) { 104 Intent areaInfoIntent = new Intent( 105 CellBroadcastAlertService.CB_AREA_INFO_RECEIVED_ACTION); 106 areaInfoIntent.putExtra("message", message); 107 context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL, 108 android.Manifest.permission.READ_PHONE_STATE); 109 } 110 } else { 111 Log.e(TAG, "caller missing READ_PHONE_STATE permission, returning"); 112 } 113 } else { 114 Log.w(TAG, "onReceive() unexpected action " + action); 115 } 116 } 117 118 /** 119 * Handle Service Category Program Data message. 120 * TODO: Send Service Category Program Results response message to sender 121 * 122 * @param context 123 * @param programDataList 124 */ 125 private void handleCdmaSmsCbProgramData(Context context, 126 CdmaSmsCbProgramData[] programDataList) { 127 for (CdmaSmsCbProgramData programData : programDataList) { 128 switch (programData.getOperation()) { 129 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: 130 tryCdmaSetCategory(context, programData.getCategory(), true); 131 break; 132 133 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: 134 tryCdmaSetCategory(context, programData.getCategory(), false); 135 break; 136 137 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: 138 tryCdmaSetCategory(context, 139 SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); 140 tryCdmaSetCategory(context, 141 SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); 142 tryCdmaSetCategory(context, 143 SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); 144 tryCdmaSetCategory(context, 145 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); 146 break; 147 148 default: 149 loge("Ignoring unknown SCPD operation " + programData.getOperation()); 150 } 151 } 152 } 153 154 private void tryCdmaSetCategory(Context context, int category, boolean enable) { 155 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 156 157 switch (category) { 158 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 159 sharedPrefs.edit().putBoolean( 160 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable) 161 .apply(); 162 break; 163 164 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 165 sharedPrefs.edit().putBoolean( 166 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable) 167 .apply(); 168 break; 169 170 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 171 sharedPrefs.edit().putBoolean( 172 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply(); 173 break; 174 175 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 176 sharedPrefs.edit().putBoolean( 177 CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply(); 178 break; 179 180 default: 181 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable") 182 + " alerts in category " + category); 183 } 184 } 185 186 /** 187 * Tell {@link CellBroadcastConfigService} to enable the CB channels. 188 * @param context the broadcast receiver context 189 */ 190 static void startConfigService(Context context) { 191 Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 192 null, context, CellBroadcastConfigService.class); 193 context.startService(serviceIntent); 194 } 195 196 /** 197 * @return true if the phone is a CDMA phone type 198 */ 199 static boolean phoneIsCdma() { 200 boolean isCdma = false; 201 try { 202 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 203 if (phone != null) { 204 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA); 205 } 206 } catch (RemoteException e) { 207 Log.w(TAG, "phone.getActivePhoneType() failed", e); 208 } 209 return isCdma; 210 } 211 212 private static void log(String msg) { 213 Log.d(TAG, msg); 214 } 215 216 private static void loge(String msg) { 217 Log.e(TAG, msg); 218 } 219 } 220