| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.cellbroadcastreceiver; |
| |
| import android.app.Activity; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.SharedPreferences; |
| import android.os.Bundle; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.preference.PreferenceManager; |
| import android.provider.Telephony; |
| import android.telephony.TelephonyManager; |
| import android.telephony.cdma.CdmaSmsCbProgramData; |
| import android.telephony.cdma.CdmaSmsCbProgramResults; |
| import android.util.Log; |
| |
| import com.android.internal.telephony.ITelephony; |
| import com.android.internal.telephony.cdma.sms.SmsEnvelope; |
| |
| import java.util.ArrayList; |
| |
| public class CellBroadcastReceiver extends BroadcastReceiver { |
| private static final String TAG = "CellBroadcastReceiver"; |
| static final boolean DBG = true; // STOPSHIP: change to false before ship |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| onReceiveWithPrivilege(context, intent, false); |
| } |
| |
| protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { |
| if (DBG) log("onReceive " + intent); |
| |
| String action = intent.getAction(); |
| |
| if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { |
| startConfigService(context); |
| } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { |
| boolean airplaneModeOn = intent.getBooleanExtra("state", false); |
| if (!airplaneModeOn) { |
| startConfigService(context); |
| } |
| } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) || |
| Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) { |
| // If 'privileged' is false, it means that the intent was delivered to the base |
| // no-permissions receiver class. If we get an SMS_CB_RECEIVED message that way, it |
| // means someone has tried to spoof the message by delivering it outside the normal |
| // permission-checked route, so we just ignore it. |
| if (privileged) { |
| intent.setClass(context, CellBroadcastAlertService.class); |
| context.startService(intent); |
| } else { |
| Log.e(TAG, "ignoring unprivileged action received " + action); |
| } |
| } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION |
| .equals(action)) { |
| if (privileged) { |
| String sender = intent.getStringExtra("sender"); |
| if (sender == null) { |
| Log.e(TAG, "SCPD intent received with no originating address"); |
| return; |
| } |
| |
| ArrayList<CdmaSmsCbProgramData> programData = |
| intent.getParcelableArrayListExtra("program_data"); |
| if (programData == null) { |
| Log.e(TAG, "SCPD intent received with no program_data"); |
| return; |
| } |
| |
| ArrayList<CdmaSmsCbProgramResults> results = handleCdmaSmsCbProgramData(context, |
| programData); |
| Bundle extras = new Bundle(); |
| extras.putString("sender", sender); |
| extras.putParcelableArrayList("results", results); |
| setResult(Activity.RESULT_OK, null, extras); |
| } else { |
| Log.e(TAG, "ignoring unprivileged action received " + action); |
| } |
| } else { |
| Log.w(TAG, "onReceive() unexpected action " + action); |
| } |
| } |
| |
| /** |
| * Handle Service Category Program Data message and return responses. |
| * |
| * @param context the context to use |
| * @param programDataList an array of SCPD operations |
| * @return the SCP results ArrayList to send to the message center |
| */ |
| private static ArrayList<CdmaSmsCbProgramResults> handleCdmaSmsCbProgramData(Context context, |
| ArrayList<CdmaSmsCbProgramData> programDataList) { |
| ArrayList<CdmaSmsCbProgramResults> results |
| = new ArrayList<CdmaSmsCbProgramResults>(programDataList.size()); |
| |
| for (CdmaSmsCbProgramData programData : programDataList) { |
| int result; |
| switch (programData.getOperation()) { |
| case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY: |
| result = tryCdmaSetCategory(context, programData.getCategory(), true); |
| break; |
| |
| case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY: |
| result = tryCdmaSetCategory(context, programData.getCategory(), false); |
| break; |
| |
| case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES: |
| tryCdmaSetCategory(context, |
| SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false); |
| tryCdmaSetCategory(context, |
| SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false); |
| tryCdmaSetCategory(context, |
| SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false); |
| tryCdmaSetCategory(context, |
| SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false); |
| result = CdmaSmsCbProgramResults.RESULT_SUCCESS; |
| break; |
| |
| default: |
| Log.e(TAG, "Ignoring unknown SCPD operation " + programData.getOperation()); |
| result = CdmaSmsCbProgramResults.RESULT_UNSPECIFIED_FAILURE; |
| } |
| results.add(new CdmaSmsCbProgramResults(programData.getCategory(), |
| programData.getLanguage(), result)); |
| } |
| |
| return results; |
| } |
| |
| /** |
| * Enables or disables a CMAS category. |
| * @param context the context to use |
| * @param category the CDMA service category |
| * @param enable true to enable; false to disable |
| * @return the service category program result code for this request |
| */ |
| private static int tryCdmaSetCategory(Context context, int category, boolean enable) { |
| String key; |
| switch (category) { |
| case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: |
| key = CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS; |
| break; |
| |
| case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: |
| key = CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS; |
| break; |
| |
| case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: |
| key = CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS; |
| break; |
| |
| case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: |
| key = CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS; |
| break; |
| |
| default: |
| Log.w(TAG, "SCPD category " + category + " is unknown, not setting to " + enable); |
| return CdmaSmsCbProgramResults.RESULT_UNSPECIFIED_FAILURE; |
| } |
| |
| SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); |
| |
| // default value is opt-in for all categories except for test messages. |
| boolean oldValue = sharedPrefs.getBoolean(key, |
| (category != SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE)); |
| |
| if (enable && oldValue) { |
| Log.d(TAG, "SCPD category " + category + " is already enabled."); |
| return CdmaSmsCbProgramResults.RESULT_CATEGORY_ALREADY_ADDED; |
| } else if (!enable && !oldValue) { |
| Log.d(TAG, "SCPD category " + category + " is already disabled."); |
| return CdmaSmsCbProgramResults.RESULT_CATEGORY_ALREADY_DELETED; |
| } else { |
| Log.d(TAG, "SCPD category " + category + " is now " + enable); |
| sharedPrefs.edit().putBoolean(key, enable).apply(); |
| return CdmaSmsCbProgramResults.RESULT_SUCCESS; |
| } |
| } |
| |
| /** |
| * Tell {@link CellBroadcastConfigService} to enable the CB channels. |
| * @param context the broadcast receiver context |
| */ |
| static void startConfigService(Context context) { |
| if (phoneIsCdma()) { |
| if (DBG) log("CDMA phone detected; doing nothing"); |
| } else { |
| Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, |
| null, context, CellBroadcastConfigService.class); |
| context.startService(serviceIntent); |
| } |
| } |
| |
| /** |
| * @return true if the phone is a CDMA phone type |
| */ |
| private static boolean phoneIsCdma() { |
| boolean isCdma = false; |
| try { |
| ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); |
| if (phone != null) { |
| isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA); |
| } |
| } catch (RemoteException e) { |
| Log.w(TAG, "phone.getActivePhoneType() failed", e); |
| } |
| return isCdma; |
| } |
| |
| private static void log(String msg) { |
| Log.d(TAG, msg); |
| } |
| } |