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