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.Bundle;
     25 import android.os.RemoteException;
     26 import android.os.ServiceManager;
     27 import android.os.UserHandle;
     28 import android.preference.PreferenceManager;
     29 import android.provider.Telephony;
     30 import android.telephony.CellBroadcastMessage;
     31 import android.telephony.ServiceState;
     32 import android.telephony.SubscriptionInfo;
     33 import android.telephony.SubscriptionManager;
     34 import android.telephony.TelephonyManager;
     35 import android.telephony.cdma.CdmaSmsCbProgramData;
     36 import android.util.Log;
     37 import com.android.internal.telephony.PhoneConstants;
     38 
     39 import com.android.internal.telephony.ITelephony;
     40 import com.android.internal.telephony.IccCardConstants;
     41 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
     42 import com.android.internal.telephony.TelephonyIntents;
     43 import com.android.internal.telephony.uicc.IccCardProxy;
     44 
     45 import java.util.List;
     46 
     47 public class CellBroadcastReceiver extends BroadcastReceiver {
     48     private static final String TAG = "CellBroadcastReceiver";
     49     static final boolean DBG = false;    // STOPSHIP: change to false before ship
     50     private static int mServiceState = -1;
     51     private static final String GET_LATEST_CB_AREA_INFO_ACTION =
     52             "android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO";
     53 
     54     @Override
     55     public void onReceive(Context context, Intent intent) {
     56         onReceiveWithPrivilege(context, intent, false);
     57     }
     58 
     59     protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
     60         if (DBG) log("onReceive " + intent);
     61 
     62         String action = intent.getAction();
     63 
     64         if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
     65             if (DBG) log("Intent ACTION_SERVICE_STATE_CHANGED");
     66             int subId = intent.getExtras().getInt(PhoneConstants.SUBSCRIPTION_KEY);
     67             Log.d(TAG, "subscriptionId = " + subId);
     68             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
     69                 return;
     70             }
     71             ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
     72             int newState = serviceState.getState();
     73             if (newState != mServiceState) {
     74                 Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState +
     75                         " Current state=" + mServiceState);
     76                 mServiceState = newState;
     77 
     78                 if (((newState == ServiceState.STATE_IN_SERVICE) ||
     79                         (newState == ServiceState.STATE_EMERGENCY_ONLY)) &&
     80                         (UserHandle.myUserId() == UserHandle.USER_OWNER)) {
     81                     startConfigService(context.getApplicationContext(), subId);
     82                 }
     83             }
     84         } else if (IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED.equals(action)){
     85             String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
     86             if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
     87                 List<SubscriptionInfo> subscriptionInfoList = SubscriptionManager.from(
     88                         context).getActiveSubscriptionInfoList();
     89                 if (subscriptionInfoList != null) {
     90                     for (SubscriptionInfo subInfo : subscriptionInfoList) {
     91                         startConfigService(context, subInfo.getSubscriptionId());
     92                     }
     93                 }
     94             }
     95         } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
     96                 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
     97             // If 'privileged' is false, it means that the intent was delivered to the base
     98             // no-permissions receiver class.  If we get an SMS_CB_RECEIVED message that way, it
     99             // means someone has tried to spoof the message by delivering it outside the normal
    100             // permission-checked route, so we just ignore it.
    101             if (privileged) {
    102                 intent.setClass(context, CellBroadcastAlertService.class);
    103                 context.startService(intent);
    104             } else {
    105                 loge("ignoring unprivileged action received " + action);
    106             }
    107         } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
    108                 .equals(action)) {
    109             if (privileged) {
    110                 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[])
    111                         intent.getParcelableArrayExtra("program_data_list");
    112                 if (programDataList != null) {
    113                     int subId = intent.getExtras().getInt(PhoneConstants.SUBSCRIPTION_KEY);
    114                     Log.d(TAG, "subscriptionId = " + subId);
    115                     handleCdmaSmsCbProgramData(context, programDataList, subId);
    116                 } else {
    117                     loge("SCPD intent received with no program_data_list");
    118                 }
    119             } else {
    120                 loge("ignoring unprivileged action received " + action);
    121             }
    122         } else if (GET_LATEST_CB_AREA_INFO_ACTION.equals(action)) {
    123             if (privileged) {
    124                 CellBroadcastMessage message = CellBroadcastReceiverApp.getLatestAreaInfo();
    125                 if (message != null) {
    126                     Intent areaInfoIntent = new Intent(
    127                             CellBroadcastAlertService.CB_AREA_INFO_RECEIVED_ACTION);
    128                     areaInfoIntent.putExtra("message", message);
    129                     // Send broadcast twice, once for apps that have PRIVILEGED permission and once
    130                     // for those that have the runtime one
    131                     context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
    132                             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
    133                     context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
    134                             android.Manifest.permission.READ_PHONE_STATE);
    135                 }
    136             } else {
    137                 Log.e(TAG, "caller missing READ_PHONE_STATE permission, returning");
    138             }
    139         } else {
    140             Log.w(TAG, "onReceive() unexpected action " + action);
    141         }
    142     }
    143 
    144     /**
    145      * Handle Service Category Program Data message.
    146      * TODO: Send Service Category Program Results response message to sender
    147      *
    148      * @param context
    149      * @param programDataList
    150      */
    151     private void handleCdmaSmsCbProgramData(Context context,
    152             CdmaSmsCbProgramData[] programDataList, int subId) {
    153         for (CdmaSmsCbProgramData programData : programDataList) {
    154             switch (programData.getOperation()) {
    155                 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
    156                     tryCdmaSetCategory(context, programData.getCategory(), true, subId);
    157                     break;
    158 
    159                 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
    160                     tryCdmaSetCategory(context, programData.getCategory(), false, subId);
    161                     break;
    162 
    163                 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
    164                     tryCdmaSetCategory(context,
    165                             SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false, subId);
    166                     tryCdmaSetCategory(context,
    167                             SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false, subId);
    168                     tryCdmaSetCategory(context,
    169                             SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false,
    170                             subId);
    171                     tryCdmaSetCategory(context,
    172                             SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false, subId);
    173                     break;
    174 
    175                 default:
    176                     loge("Ignoring unknown SCPD operation " + programData.getOperation());
    177             }
    178         }
    179     }
    180 
    181     private void tryCdmaSetCategory(Context context, int category, boolean enable, int subId) {
    182         switch (category) {
    183             case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
    184                 SubscriptionManager.setSubscriptionProperty(subId,
    185                         SubscriptionManager.CB_EXTREME_THREAT_ALERT,
    186                         (enable ? "1" : "0"));
    187                 break;
    188 
    189             case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
    190                 SubscriptionManager.setSubscriptionProperty(subId,
    191                         SubscriptionManager.CB_SEVERE_THREAT_ALERT,
    192                         (enable ? "1" : "0"));
    193                 break;
    194 
    195             case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
    196                 SubscriptionManager.setSubscriptionProperty(subId,
    197                         SubscriptionManager.CB_AMBER_ALERT,
    198                         (enable ? "1" : "0"));
    199                 break;
    200 
    201             case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
    202                 SubscriptionManager.setSubscriptionProperty(subId,
    203                         SubscriptionManager.CB_CMAS_TEST_ALERT,
    204                         (enable ? "1" : "0"));
    205                 break;
    206 
    207             default:
    208                 Log.w(TAG, "Ignoring SCPD command to " + (enable ? "enable" : "disable")
    209                         + " alerts in category " + category);
    210         }
    211     }
    212 
    213     /**
    214      * Tell {@link CellBroadcastConfigService} to enable the CB channels.
    215      * @param context the broadcast receiver context
    216      */
    217     static void startConfigService(Context context, int subId) {
    218         Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS,
    219                 null, context, CellBroadcastConfigService.class);
    220         serviceIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
    221         context.startService(serviceIntent);
    222     }
    223 
    224     /**
    225      * @return true if the phone is a CDMA phone type
    226      */
    227     static boolean phoneIsCdma(int subId) {
    228         boolean isCdma = false;
    229         try {
    230             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
    231             if (phone != null) {
    232                 isCdma = (phone.getActivePhoneTypeForSubscriber(subId) ==
    233                         TelephonyManager.PHONE_TYPE_CDMA);
    234             }
    235         } catch (RemoteException e) {
    236             Log.w(TAG, "phone.getActivePhoneType() failed", e);
    237         }
    238         return isCdma;
    239     }
    240 
    241     private static void log(String msg) {
    242         Log.d(TAG, msg);
    243     }
    244 
    245     private static void loge(String msg) {
    246         Log.e(TAG, msg);
    247     }
    248 }
    249