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                         (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