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