CellBroadcastConfigService.java revision e348e51fedf48b9d07f5f737c0ae2ca10acdd356
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
17package com.android.cellbroadcastreceiver;
18
19import android.app.IntentService;
20import android.content.Context;
21import android.content.Intent;
22import android.content.SharedPreferences;
23import android.content.res.Resources;
24import android.os.SystemProperties;
25import android.preference.PreferenceManager;
26import android.telephony.CellBroadcastMessage;
27import android.telephony.SmsManager;
28import android.telephony.TelephonyManager;
29import android.text.TextUtils;
30import android.util.Log;
31
32import com.android.internal.telephony.cdma.sms.SmsEnvelope;
33import com.android.internal.telephony.gsm.SmsCbConstants;
34
35import 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 */
48public 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 aplicable for cdma");
273                } else if (enableChannel50Alerts) {
274                    if (DBG) log("enabling cell broadcast channel 50");
275                    manager.enableCellBroadcast(50);
276                    if (DBG) log("enabled cell broadcast channel 50");
277                } else {
278                    if (DBG) log("disabling cell broadcast channel 50");
279                    manager.disableCellBroadcast(50);
280                    if (DBG) log("disabled cell broadcast channel 50");
281                }
282
283                // Disable per user preference/checkbox.
284                // This takes care of the case where enableEmergencyAlerts is true,
285                // but check box is unchecked to receive such as cmas severe alerts.
286                if (!enableEtwsTestAlerts  && !isCdma) {
287                    if (DBG) Log.d(TAG, "disabling cell broadcast ETWS test messages");
288                    manager.disableCellBroadcast(
289                            SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE);
290                }
291                if (!enableCmasExtremeAlerts) {
292                    if (DBG) Log.d(TAG, "disabling cell broadcast CMAS extreme");
293                    manager.disableCellBroadcastRange(cmasExtremeStart, cmasExtremeEnd);
294                }
295                if (!enableCmasSevereAlerts) {
296                    if (DBG) Log.d(TAG, "disabling cell broadcast CMAS severe");
297                    manager.disableCellBroadcastRange(cmasSevereStart, cmasSevereEnd);
298                }
299                if (!enableCmasAmberAlerts) {
300                    if (DBG) Log.d(TAG, "disabling cell broadcast CMAS amber");
301                    manager.disableCellBroadcast(cmasAmber);
302                }
303                if (!enableCmasTestAlerts) {
304                    if (DBG) Log.d(TAG, "disabling cell broadcast CMAS test messages");
305                    manager.disableCellBroadcastRange(cmasTestStart, cmasTestEnd);
306                }
307            } catch (Exception ex) {
308                Log.e(TAG, "exception enabling cell broadcast channels", ex);
309            }
310        }
311    }
312
313    private static void log(String msg) {
314        Log.d(TAG, msg);
315    }
316}
317