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