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