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.preference.PreferenceManager; 26 import android.provider.Telephony; 27 import android.telephony.TelephonyManager; 28 import android.telephony.cdma.CdmaSmsCbProgramData; 29 import android.util.Log; 30 31 import com.android.internal.telephony.ITelephony; 32 import com.android.internal.telephony.cdma.sms.SmsEnvelope; 33 34 public class CellBroadcastReceiver extends BroadcastReceiver { 35 private static final String TAG = "CellBroadcastReceiver"; 36 static final boolean DBG = true; // STOPSHIP: change to false before ship 37 38 @Override 39 public void onReceive(Context context, Intent intent) { 40 onReceiveWithPrivilege(context, intent, false); 41 } 42 43 protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { 44 if (DBG) log("onReceive " + intent); 45 46 String action = intent.getAction(); 47 48 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { 49 startConfigService(context); 50 } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { 51 boolean airplaneModeOn = intent.getBooleanExtra("state", false); 52 if (!airplaneModeOn) { 53 startConfigService(context); 54 } 55 } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || 56 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { 57 // If 'privileged' is false, it means that the intent was delivered to the base 58 // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it 59 // means someone has tried to spoof the message by delivering it outside the normal 60 // permission-checked route, so we just ignore it. 61 if (privileged) { 62 intent.setClass(context, CellBroadcastAlertService.class); 63 context.startService(intent); 64 } else { 65 Log.e(TAG, "ignoring unprivileged action received " + action); 66 } 67 } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION 68 .equals(action)) { 69 if (privileged) { 70 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[]) 71 intent.getParcelableArrayExtra("program_data_list"); 72 if (programDataList != null) { 73 handleCdmaSmsCbProgramData(context, programDataList); 74 } else { 75 Log.e(TAG, "SCPD intent received with no program_data_list"); 76 } 77 } else { 78 Log.e(TAG, "ignoring unprivileged action received " + action); 79 } 80 } else { 81 Log.w(TAG, "onReceive() unexpected action " + action); 82 } 83 } 84 85 /** 86 * Handle Service Category Program Data message. 87 * TODO: Send Service Category Program Results response message to sender 88 * 89 * @param context 90 * @param programDataList 91 */ 92 private void handleCdmaSmsCbProgramData(Context context, 93 CdmaSmsCbProgramData[] programDataList) { 94 for (CdmaSmsCbProgramData programData : programDataList) { 95 switch (programData.getOperation()) { 96 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: 97 tryCdmaSetCategory(context, programData.getCategory(), true); 98 break; 99 100 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: 101 tryCdmaSetCategory(context, programData.getCategory(), false); 102 break; 103 104 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: 105 tryCdmaSetCategory(context, 106 SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); 107 tryCdmaSetCategory(context, 108 SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); 109 tryCdmaSetCategory(context, 110 SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); 111 tryCdmaSetCategory(context, 112 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); 113 break; 114 115 default: 116 Log.e(TAG, "Ignoring unknown SCPD operation " + programData.getOperation()); 117 } 118 } 119 } 120 121 private void tryCdmaSetCategory(Context context, int category, boolean enable) { 122 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 123 124 switch (category) { 125 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 126 sharedPrefs.edit().putBoolean( 127 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable) 128 .apply(); 129 break; 130 131 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 132 sharedPrefs.edit().putBoolean( 133 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable) 134 .apply(); 135 break; 136 137 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 138 sharedPrefs.edit().putBoolean( 139 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply(); 140 break; 141 142 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 143 sharedPrefs.edit().putBoolean( 144 CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply(); 145 break; 146 147 default: 148 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable") 149 + " alerts in category " + category); 150 } 151 } 152 153 /** 154 * Tell {@link CellBroadcastConfigService} to enable the CB channels. 155 * @param context the broadcast receiver context 156 */ 157 static void startConfigService(Context context) { 158 if (phoneIsCdma()) { 159 if (DBG) log("CDMA phone detected; doing nothing"); 160 } else { 161 Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 162 null, context, CellBroadcastConfigService.class); 163 context.startService(serviceIntent); 164 } 165 } 166 167 /** 168 * @return true if the phone is a CDMA phone type 169 */ 170 private static boolean phoneIsCdma() { 171 boolean isCdma = false; 172 try { 173 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 174 if (phone != null) { 175 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA); 176 } 177 } catch (RemoteException e) { 178 Log.w(TAG, "phone.getActivePhoneType() failed", e); 179 } 180 return isCdma; 181 } 182 183 private static void log(String msg) { 184 Log.d(TAG, msg); 185 } 186 } 187