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.SharedPreferences.Editor;
     24 import android.os.UserManager;
     25 import android.preference.PreferenceManager;
     26 import android.provider.Telephony;
     27 import android.telephony.ServiceState;
     28 import android.telephony.cdma.CdmaSmsCbProgramData;
     29 import android.util.Log;
     30 
     31 import com.android.internal.telephony.IccCardConstants;
     32 import com.android.internal.telephony.TelephonyIntents;
     33 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
     34 
     35 public class CellBroadcastReceiver extends BroadcastReceiver {
     36     private static final String TAG = "CellBroadcastReceiver";
     37     static final boolean DBG = false;    // STOPSHIP: change to false before ship
     38     private static int mServiceState = -1;
     39 
     40     public static final String CELLBROADCAST_START_CONFIG_ACTION =
     41             "android.cellbroadcastreceiver.START_CONFIG";
     42 
     43     // Key to access the stored reminder interval default value
     44     private static final String CURRENT_INTERVAL_DEFAULT = "current_interval_default";
     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);
     58             ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
     59             if (serviceState != null) {
     60                 int newState = serviceState.getState();
     61                 if (newState != mServiceState) {
     62                     Log.d(TAG, "Service state changed! " + newState + " Full: " + serviceState +
     63                             " Current state=" + mServiceState);
     64                     mServiceState = newState;
     65                     if (((newState == ServiceState.STATE_IN_SERVICE) ||
     66                             (newState == ServiceState.STATE_EMERGENCY_ONLY)) &&
     67                             (UserManager.get(context).isSystemUser())) {
     68                         startConfigService(context.getApplicationContext());
     69                     }
     70                 }
     71             }
     72         } else if (TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED.equals(action) ||
     73                 CELLBROADCAST_START_CONFIG_ACTION.equals(action)) {
     74             // Todo: Add the service state check once the new get service state API is done.
     75             // Do not rely on mServiceState as it gets reset to -1 time to time because
     76             // the process of CellBroadcastReceiver gets killed every time once the job is done.
     77             if (UserManager.get(context).isSystemUser()) {
     78                 startConfigService(context.getApplicationContext());
     79             }
     80             else {
     81                 Log.e(TAG, "Not system user. Ignored the intent " + action);
     82             }
     83         } else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
     84                 Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
     85             // If 'privileged' is false, it means that the intent was delivered to the base
     86             // no-permissions receiver class.  If we get an SMS_CB_RECEIVED message that way, it
     87             // means someone has tried to spoof the message by delivering it outside the normal
     88             // permission-checked route, so we just ignore it.
     89             if (privileged) {
     90                 intent.setClass(context, CellBroadcastAlertService.class);
     91                 context.startService(intent);
     92             } else {
     93                 loge("ignoring unprivileged action received " + action);
     94             }
     95         } else if (Telephony.Sms.Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION
     96                 .equals(action)) {
     97             if (privileged) {
     98                 CdmaSmsCbProgramData[] programDataList = (CdmaSmsCbProgramData[])
     99                         intent.getParcelableArrayExtra("program_data_list");
    100                 if (programDataList != null) {
    101                     handleCdmaSmsCbProgramData(context, programDataList);
    102                 } else {
    103                     loge("SCPD intent received with no program_data_list");
    104                 }
    105             } else {
    106                 loge("ignoring unprivileged action received " + action);
    107             }
    108         } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
    109             String simState = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
    110             // Whenever sim is loaded, we need to adjust the emergency alert
    111             // reminder interval list because it might change since different
    112             // countries/carriers might have different interval settings.
    113             if (simState.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) {
    114                 adjustReminderInterval(context.getApplicationContext());
    115             }
    116         } else {
    117             Log.w(TAG, "onReceive() unexpected action " + action);
    118         }
    119     }
    120 
    121     private void adjustReminderInterval(Context context) {
    122 
    123         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
    124         String currentIntervalDefault = sp.getString(CURRENT_INTERVAL_DEFAULT, "0");
    125 
    126         // If interval default changes, reset the interval to the new default value.
    127         String newIntervalDefault = context.getResources().getString(
    128                 R.string.alert_reminder_interval_default_value);
    129         if (!newIntervalDefault.equals(currentIntervalDefault)) {
    130             Log.d(TAG, "Default interval changed from " + currentIntervalDefault + " to " +
    131                     newIntervalDefault);
    132 
    133             Editor editor = sp.edit();
    134             // Reset the value to default.
    135             editor.putString(
    136                     CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, newIntervalDefault);
    137             // Save the new default value.
    138             editor.putString(CURRENT_INTERVAL_DEFAULT, newIntervalDefault);
    139             editor.commit();
    140         } else {
    141             if (DBG) Log.d(TAG, "Default interval " + currentIntervalDefault + " did not change.");
    142         }
    143     }
    144 
    145     /**
    146      * Handle Service Category Program Data message.
    147      * TODO: Send Service Category Program Results response message to sender
    148      *
    149      * @param context
    150      * @param programDataList
    151      */
    152     private void handleCdmaSmsCbProgramData(Context context,
    153                                             CdmaSmsCbProgramData[] programDataList) {
    154         for (CdmaSmsCbProgramData programData : programDataList) {
    155             switch (programData.getOperation()) {
    156                 case CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY:
    157                     tryCdmaSetCategory(context, programData.getCategory(), true);
    158                     break;
    159 
    160                 case CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY:
    161                     tryCdmaSetCategory(context, programData.getCategory(), false);
    162                     break;
    163 
    164                 case CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES:
    165                     tryCdmaSetCategory(context,
    166                             SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, false);
    167                     tryCdmaSetCategory(context,
    168                             SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, false);
    169                     tryCdmaSetCategory(context,
    170                             SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY, false);
    171                     tryCdmaSetCategory(context,
    172                             SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE, false);
    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) {
    182         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    183 
    184         switch (category) {
    185             case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
    186                 sharedPrefs.edit().putBoolean(
    187                         CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, enable)
    188                         .apply();
    189                 break;
    190 
    191             case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
    192                 sharedPrefs.edit().putBoolean(
    193                         CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, enable)
    194                         .apply();
    195                 break;
    196 
    197             case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
    198                 sharedPrefs.edit().putBoolean(
    199                         CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, enable).apply();
    200                 break;
    201 
    202             case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
    203                 sharedPrefs.edit().putBoolean(
    204                         CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, enable).apply();
    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) {
    218         Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS,
    219                 null, context, CellBroadcastConfigService.class);
    220         Log.d(TAG, "Start Cell Broadcast configuration.");
    221         context.startService(serviceIntent);
    222     }
    223 
    224     private static void log(String msg) {
    225         Log.d(TAG, msg);
    226     }
    227 
    228     private static void loge(String msg) {
    229         Log.e(TAG, msg);
    230     }
    231 }
    232