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.app.IntentService;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.SharedPreferences;
     23 import android.content.res.Resources;
     24 import android.os.SystemProperties;
     25 import android.preference.PreferenceManager;
     26 import android.telephony.CellBroadcastMessage;
     27 import android.telephony.SmsManager;
     28 import android.telephony.TelephonyManager;
     29 import android.text.TextUtils;
     30 import android.util.Log;
     31 
     32 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
     33 import com.android.internal.telephony.gsm.SmsCbConstants;
     34 
     35 import static com.android.cellbroadcastreceiver.CellBroadcastReceiver.DBG;
     36 
     37 /**
     38  * This service manages enabling and disabling ranges of message identifiers
     39  * that the radio should listen for. It operates independently of the other
     40  * services and runs at boot time and after exiting airplane mode.
     41  *
     42  * Note that the entire range of emergency channels is enabled. Test messages
     43  * and lower priority broadcasts are filtered out in CellBroadcastAlertService
     44  * if the user has not enabled them in settings.
     45  *
     46  * TODO: add notification to re-enable channels after a radio reset.
     47  */
     48 public class CellBroadcastConfigService extends IntentService {
     49     private static final String TAG = "CellBroadcastConfigService";
     50 
     51     static final String ACTION_ENABLE_CHANNELS = "ACTION_ENABLE_CHANNELS";
     52 
     53     static final String EMERGENCY_BROADCAST_RANGE_GSM =
     54             "ro.cb.gsm.emergencyids";
     55 
     56     public CellBroadcastConfigService() {
     57         super(TAG);          // use class name for worker thread name
     58     }
     59 
     60     private static void setChannelRange(SmsManager manager, String ranges, boolean enable) {
     61         if (DBG)log("setChannelRange: " + ranges);
     62 
     63         try {
     64             for (String channelRange : ranges.split(",")) {
     65                 int dashIndex = channelRange.indexOf('-');
     66                 if (dashIndex != -1) {
     67                     int startId = Integer.decode(channelRange.substring(0, dashIndex).trim());
     68                     int endId = Integer.decode(channelRange.substring(dashIndex + 1).trim());
     69                     if (enable) {
     70                         if (DBG) log("enabling emergency IDs " + startId + '-' + endId);
     71                         manager.enableCellBroadcastRange(startId, endId);
     72                     } else {
     73                         if (DBG) log("disabling emergency IDs " + startId + '-' + endId);
     74                         manager.disableCellBroadcastRange(startId, endId);
     75                     }
     76                 } else {
     77                     int messageId = Integer.decode(channelRange.trim());
     78                     if (enable) {
     79                         if (DBG) log("enabling emergency message ID " + messageId);
     80                         manager.enableCellBroadcast(messageId);
     81                     } else {
     82                         if (DBG) log("disabling emergency message ID " + messageId);
     83                         manager.disableCellBroadcast(messageId);
     84                     }
     85                 }
     86             }
     87         } catch (NumberFormatException e) {
     88             Log.e(TAG, "Number Format Exception parsing emergency channel range", e);
     89         }
     90 
     91         // Make sure CMAS Presidential is enabled (See 3GPP TS 22.268 Section 6.2).
     92         if (DBG) log("setChannelRange: enabling CMAS Presidential");
     93         if (CellBroadcastReceiver.phoneIsCdma()) {
     94             manager.enableCellBroadcast(SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT);
     95         } else {
     96             manager.enableCellBroadcast(SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL);
     97         }
     98     }
     99 
    100     /**
    101      * Returns true if this is a standard or operator-defined emergency alert message.
    102      * This includes all ETWS and CMAS alerts, except for AMBER alerts.
    103      * @param message the message to test
    104      * @return true if the message is an emergency alert; false otherwise
    105      */
    106     static boolean isEmergencyAlertMessage(CellBroadcastMessage message) {
    107         if (message.isEmergencyAlertMessage()) {
    108             return true;
    109         }
    110 
    111         // Check for system property defining the emergency channel ranges to enable
    112         String emergencyIdRange = (CellBroadcastReceiver.phoneIsCdma()) ?
    113                 "" : SystemProperties.get(EMERGENCY_BROADCAST_RANGE_GSM);
    114 
    115         if (TextUtils.isEmpty(emergencyIdRange)) {
    116             return false;
    117         }
    118         try {
    119             int messageId = message.getServiceCategory();
    120             for (String channelRange : emergencyIdRange.split(",")) {
    121                 int dashIndex = channelRange.indexOf('-');
    122                 if (dashIndex != -1) {
    123                     int startId = Integer.decode(channelRange.substring(0, dashIndex).trim());
    124                     int endId = Integer.decode(channelRange.substring(dashIndex + 1).trim());
    125                     if (messageId >= startId && messageId <= endId) {
    126                         return true;
    127                     }
    128                 } else {
    129                     int emergencyMessageId = Integer.decode(channelRange.trim());
    130                     if (emergencyMessageId == messageId) {
    131                         return true;
    132                     }
    133                 }
    134             }
    135         } catch (NumberFormatException e) {
    136             Log.e(TAG, "Number Format Exception parsing emergency channel range", e);
    137         }
    138         return false;
    139     }
    140 
    141     @Override
    142     protected void onHandleIntent(Intent intent) {
    143         if (ACTION_ENABLE_CHANNELS.equals(intent.getAction())) {
    144             try {
    145                 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
    146                 Resources res = getResources();
    147 
    148                 // boolean for each user preference checkbox, true for checked, false for unchecked
    149                 // Note: If enableEmergencyAlerts is false, it disables ALL emergency broadcasts
    150                 // except for cmas presidential. i.e. to receive cmas severe alerts, both
    151                 // enableEmergencyAlerts AND enableCmasSevereAlerts must be true.
    152                 boolean enableEmergencyAlerts = prefs.getBoolean(
    153                         CellBroadcastSettings.KEY_ENABLE_EMERGENCY_ALERTS, true);
    154 
    155                 TelephonyManager tm = (TelephonyManager) getSystemService(
    156                         Context.TELEPHONY_SERVICE);
    157 
    158                 boolean enableChannel50Support = res.getBoolean(R.bool.show_brazil_settings) ||
    159                         "br".equals(tm.getSimCountryIso());
    160 
    161                 boolean enableChannel50Alerts = enableChannel50Support &&
    162                         prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_CHANNEL_50_ALERTS, true);
    163 
    164                 // Note:  ETWS is for 3GPP only
    165                 boolean enableEtwsTestAlerts = prefs.getBoolean(
    166                         CellBroadcastSettings.KEY_ENABLE_ETWS_TEST_ALERTS, false);
    167 
    168                 boolean enableCmasExtremeAlerts = prefs.getBoolean(
    169                         CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, true);
    170 
    171                 boolean enableCmasSevereAlerts = prefs.getBoolean(
    172                         CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, true);
    173 
    174                 boolean enableCmasAmberAlerts = prefs.getBoolean(
    175                         CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, true);
    176 
    177                 boolean enableCmasTestAlerts = prefs.getBoolean(
    178                         CellBroadcastSettings.KEY_ENABLE_CMAS_TEST_ALERTS, false);
    179 
    180                 // set up broadcast ID ranges to be used for each category
    181                 int cmasExtremeStart =
    182                         SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED;
    183                 int cmasExtremeEnd = SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY;
    184                 int cmasSevereStart =
    185                         SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED;
    186                 int cmasSevereEnd = SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY;
    187                 int cmasAmber = SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY;
    188                 int cmasTestStart = SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST;
    189                 int cmasTestEnd = SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE;
    190                 int cmasPresident = SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL;
    191 
    192                 // set to CDMA broadcast ID rage if phone is in CDMA mode.
    193                 boolean isCdma = CellBroadcastReceiver.phoneIsCdma();
    194                 if (isCdma) {
    195                     cmasExtremeStart = SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT;
    196                     cmasExtremeEnd = cmasExtremeStart;
    197                     cmasSevereStart = SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT;
    198                     cmasSevereEnd = cmasSevereStart;
    199                     cmasAmber = SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY;
    200                     cmasTestStart = SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE;
    201                     cmasTestEnd = cmasTestStart;
    202                     cmasPresident = SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
    203                 }
    204 
    205                 SmsManager manager = SmsManager.getDefault();
    206                 // Check for system property defining the emergency channel ranges to enable
    207                 String emergencyIdRange = isCdma ?
    208                         "" : SystemProperties.get(EMERGENCY_BROADCAST_RANGE_GSM);
    209                 if (enableEmergencyAlerts) {
    210                     if (DBG) log("enabling emergency cell broadcast channels");
    211                     if (!TextUtils.isEmpty(emergencyIdRange)) {
    212                         setChannelRange(manager, emergencyIdRange, true);
    213                     } else {
    214                         // No emergency channel system property, enable all emergency channels
    215                         // that have checkbox checked
    216                         if (!isCdma) {
    217                             manager.enableCellBroadcastRange(
    218                                     SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING,
    219                                     SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING);
    220                             if (enableEtwsTestAlerts) {
    221                                 manager.enableCellBroadcast(
    222                                         SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE);
    223                             }
    224                             manager.enableCellBroadcast(
    225                                     SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE);
    226                         }
    227                         if (enableCmasExtremeAlerts) {
    228                             manager.enableCellBroadcastRange(cmasExtremeStart, cmasExtremeEnd);
    229                         }
    230                         if (enableCmasSevereAlerts) {
    231                             manager.enableCellBroadcastRange(cmasSevereStart, cmasSevereEnd);
    232                         }
    233                         if (enableCmasAmberAlerts) {
    234                             manager.enableCellBroadcast(cmasAmber);
    235                         }
    236                         if (enableCmasTestAlerts) {
    237                             manager.enableCellBroadcastRange(cmasTestStart, cmasTestEnd);
    238                         }
    239                         // CMAS Presidential must be on (See 3GPP TS 22.268 Section 6.2).
    240                         manager.enableCellBroadcast(cmasPresident);
    241                     }
    242                     if (DBG) log("enabled emergency cell broadcast channels");
    243                 } else {
    244                     // we may have enabled these channels previously, so try to disable them
    245                     if (DBG) log("disabling emergency cell broadcast channels");
    246                     if (!TextUtils.isEmpty(emergencyIdRange)) {
    247                         setChannelRange(manager, emergencyIdRange, false);
    248                     } else {
    249                         // No emergency channel system property, disable all emergency channels
    250                         // except for CMAS Presidential (See 3GPP TS 22.268 Section 6.2)
    251                         if (!isCdma) {
    252                             manager.disableCellBroadcastRange(
    253                                     SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING,
    254                                     SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING);
    255                             manager.disableCellBroadcast(
    256                                     SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE);
    257                             manager.disableCellBroadcast(
    258                                     SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE);
    259                         }
    260                         manager.disableCellBroadcastRange(cmasExtremeStart, cmasExtremeEnd);
    261                         manager.disableCellBroadcastRange(cmasSevereStart, cmasSevereEnd);
    262                         manager.disableCellBroadcast(cmasAmber);
    263                         manager.disableCellBroadcastRange(cmasTestStart, cmasTestEnd);
    264 
    265                         // CMAS Presidential must be on (See 3GPP TS 22.268 Section 6.2).
    266                         manager.enableCellBroadcast(cmasPresident);
    267                     }
    268                     if (DBG) log("disabled emergency cell broadcast channels");
    269                 }
    270 
    271                 if (isCdma) {
    272                     if (DBG) log("channel 50 is not applicable for cdma");
    273                 } else if (enableChannel50Alerts) {
    274                     if (DBG) log("enabling cell broadcast channel 50");
    275                     manager.enableCellBroadcast(50);
    276                 } else {
    277                     if (DBG) log("disabling cell broadcast channel 50");
    278                     manager.disableCellBroadcast(50);
    279                 }
    280 
    281                 if ("il".equals(tm.getSimCountryIso()) || "il".equals(tm.getNetworkCountryIso())) {
    282                     if (DBG) log("enabling channels 919-928 for Israel");
    283                     manager.enableCellBroadcastRange(919, 928);
    284                 } else {
    285                     if (DBG) log("disabling channels 919-928");
    286                     manager.disableCellBroadcastRange(919, 928);
    287                 }
    288 
    289                 // Disable per user preference/checkbox.
    290                 // This takes care of the case where enableEmergencyAlerts is true,
    291                 // but check box is unchecked to receive such as cmas severe alerts.
    292                 if (!enableEtwsTestAlerts  && !isCdma) {
    293                     if (DBG) Log.d(TAG, "disabling cell broadcast ETWS test messages");
    294                     manager.disableCellBroadcast(
    295                             SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE);
    296                 }
    297                 if (!enableCmasExtremeAlerts) {
    298                     if (DBG) Log.d(TAG, "disabling cell broadcast CMAS extreme");
    299                     manager.disableCellBroadcastRange(cmasExtremeStart, cmasExtremeEnd);
    300                 }
    301                 if (!enableCmasSevereAlerts) {
    302                     if (DBG) Log.d(TAG, "disabling cell broadcast CMAS severe");
    303                     manager.disableCellBroadcastRange(cmasSevereStart, cmasSevereEnd);
    304                 }
    305                 if (!enableCmasAmberAlerts) {
    306                     if (DBG) Log.d(TAG, "disabling cell broadcast CMAS amber");
    307                     manager.disableCellBroadcast(cmasAmber);
    308                 }
    309                 if (!enableCmasTestAlerts) {
    310                     if (DBG) Log.d(TAG, "disabling cell broadcast CMAS test messages");
    311                     manager.disableCellBroadcastRange(cmasTestStart, cmasTestEnd);
    312                 }
    313             } catch (Exception ex) {
    314                 Log.e(TAG, "exception enabling cell broadcast channels", ex);
    315             }
    316         }
    317     }
    318 
    319     private static void log(String msg) {
    320         Log.d(TAG, msg);
    321     }
    322 }
    323