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