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 static com.android.cellbroadcastreceiver.CellBroadcastReceiver.VDBG;
20 
21 import android.app.IntentService;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.SharedPreferences;
25 import android.content.res.Resources;
26 import android.preference.PreferenceManager;
27 import android.telephony.SmsManager;
28 import android.telephony.SubscriptionInfo;
29 import android.telephony.SubscriptionManager;
30 import android.util.Log;
31 
32 import androidx.annotation.NonNull;
33 
34 import com.android.cellbroadcastreceiver.CellBroadcastChannelManager.CellBroadcastChannelRange;
35 import com.android.internal.annotations.VisibleForTesting;
36 
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40 
41 /**
42  * This service manages enabling and disabling ranges of message identifiers
43  * that the radio should listen for. It operates independently of the other
44  * services and runs at boot time and after exiting airplane mode.
45  *
46  * Note that the entire range of emergency channels is enabled. Test messages
47  * and lower priority broadcasts are filtered out in CellBroadcastAlertService
48  * if the user has not enabled them in settings.
49  *
50  * TODO: add notification to re-enable channels after a radio reset.
51  */
52 public class CellBroadcastConfigService extends IntentService {
53     private static final String TAG = "CellBroadcastConfigService";
54 
55     @VisibleForTesting
56     public static final String ACTION_ENABLE_CHANNELS = "ACTION_ENABLE_CHANNELS";
57 
CellBroadcastConfigService()58     public CellBroadcastConfigService() {
59         super(TAG);          // use class name for worker thread name
60     }
61 
62     @Override
onHandleIntent(Intent intent)63     protected void onHandleIntent(Intent intent) {
64         if (ACTION_ENABLE_CHANNELS.equals(intent.getAction())) {
65             try {
66                 SubscriptionManager subManager = (SubscriptionManager) getApplicationContext()
67                         .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
68 
69                 if (subManager != null) {
70                     // Retrieve all the active subscription indice and enable cell broadcast
71                     // messages on all subs. The duplication detection will be done at the
72                     // frameworks.
73                     int[] subIds = getActiveSubIdList(subManager);
74                     if (subIds.length != 0) {
75                         for (int subId : subIds) {
76                             log("Enable CellBroadcast on sub " + subId);
77                             enableCellBroadcastChannels(subId);
78                         }
79                     } else {
80                         // For no sim scenario.
81                         enableCellBroadcastChannels(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
82                     }
83                 }
84             } catch (Exception ex) {
85                 Log.e(TAG, "exception enabling cell broadcast channels", ex);
86             }
87         }
88     }
89 
90     @NonNull
getActiveSubIdList(SubscriptionManager subMgr)91     private int[] getActiveSubIdList(SubscriptionManager subMgr) {
92         List<SubscriptionInfo> subInfos = subMgr.getActiveSubscriptionInfoList();
93         int size = subInfos != null ? subInfos.size() : 0;
94         int[] subIds = new int[size];
95         for (int i = 0; i < size; i++) {
96             subIds[i] = subInfos.get(i).getSubscriptionId();
97         }
98         return subIds;
99     }
100 
101     /**
102      * Enable cell broadcast messages channels. Messages can be only received on the
103      * enabled channels.
104      *
105      * @param subId Subscription index
106      */
107     @VisibleForTesting
enableCellBroadcastChannels(int subId)108     public void enableCellBroadcastChannels(int subId) {
109 
110         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
111         Resources res = CellBroadcastSettings.getResources(this, subId);
112 
113         // boolean for each user preference checkbox, true for checked, false for unchecked
114         // Note: If enableAlertsMasterToggle is false, it disables ALL emergency broadcasts
115         // except for CMAS presidential. i.e. to receive CMAS severe alerts, both
116         // enableAlertsMasterToggle AND enableCmasSevereAlerts must be true.
117         boolean enableAlertsMasterToggle = prefs.getBoolean(
118                 CellBroadcastSettings.KEY_ENABLE_ALERTS_MASTER_TOGGLE, true);
119 
120         boolean enableEtwsAlerts = enableAlertsMasterToggle;
121 
122         // CMAS Presidential must be always on (See 3GPP TS 22.268 Section 6.2) regardless
123         // user's preference
124         boolean enablePresidential = true;
125 
126         boolean enableCmasExtremeAlerts = enableAlertsMasterToggle && prefs.getBoolean(
127                 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, true);
128 
129         boolean enableCmasSevereAlerts = enableAlertsMasterToggle && prefs.getBoolean(
130                 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, true);
131 
132         boolean enableCmasAmberAlerts = enableAlertsMasterToggle && prefs.getBoolean(
133                 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, true);
134 
135         boolean enableTestAlerts = enableAlertsMasterToggle
136                 && CellBroadcastSettings.isTestAlertsToggleVisible(getApplicationContext())
137                 && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_TEST_ALERTS, false);
138 
139         boolean enableAreaUpdateInfoAlerts = res.getBoolean(
140                 R.bool.config_showAreaUpdateInfoSettings)
141                 && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_AREA_UPDATE_INFO_ALERTS,
142                 false);
143 
144         boolean enablePublicSafetyMessagesChannelAlerts = enableAlertsMasterToggle
145                 && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_PUBLIC_SAFETY_MESSAGES,
146                 true);
147         boolean enableStateLocalTestAlerts = enableAlertsMasterToggle
148                 && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_STATE_LOCAL_TEST_ALERTS,
149                 false);
150 
151         boolean enableEmergencyAlerts = enableAlertsMasterToggle && prefs.getBoolean(
152                 CellBroadcastSettings.KEY_ENABLE_EMERGENCY_ALERTS, true);
153 
154         boolean enableGeoFencingTriggerMessage = true;
155 
156         if (VDBG) {
157             log("enableAlertsMasterToggle = " + enableAlertsMasterToggle);
158             log("enableEtwsAlerts = " + enableEtwsAlerts);
159             log("enablePresidential = " + enablePresidential);
160             log("enableCmasExtremeAlerts = " + enableCmasExtremeAlerts);
161             log("enableCmasSevereAlerts = " + enableCmasExtremeAlerts);
162             log("enableCmasAmberAlerts = " + enableCmasAmberAlerts);
163             log("enableTestAlerts = " + enableTestAlerts);
164             log("enableAreaUpdateInfoAlerts = " + enableAreaUpdateInfoAlerts);
165             log("enablePublicSafetyMessagesChannelAlerts = "
166                     + enablePublicSafetyMessagesChannelAlerts);
167             log("enableStateLocalTestAlerts = " + enableStateLocalTestAlerts);
168             log("enableEmergencyAlerts = " + enableEmergencyAlerts);
169             log("enableGeoFencingTriggerMessage = " + enableGeoFencingTriggerMessage);
170         }
171 
172         CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager(
173                 getApplicationContext(), subId);
174 
175         /** Enable CMAS series messages. */
176 
177         // Enable/Disable Presidential messages.
178         setCellBroadcastRange(subId, enablePresidential,
179                 channelManager.getCellBroadcastChannelRanges(
180                         R.array.cmas_presidential_alerts_channels_range_strings));
181 
182         // Enable/Disable CMAS extreme messages.
183         setCellBroadcastRange(subId, enableCmasExtremeAlerts,
184                 channelManager.getCellBroadcastChannelRanges(
185                         R.array.cmas_alert_extreme_channels_range_strings));
186 
187         // Enable/Disable CMAS severe messages.
188         setCellBroadcastRange(subId, enableCmasSevereAlerts,
189                 channelManager.getCellBroadcastChannelRanges(
190                         R.array.cmas_alerts_severe_range_strings));
191 
192         // Enable/Disable CMAS amber alert messages.
193         setCellBroadcastRange(subId, enableCmasAmberAlerts,
194                 channelManager.getCellBroadcastChannelRanges(
195                         R.array.cmas_amber_alerts_channels_range_strings));
196 
197         // Enable/Disable test messages.
198         setCellBroadcastRange(subId, enableTestAlerts,
199                 channelManager.getCellBroadcastChannelRanges(
200                         R.array.required_monthly_test_range_strings));
201 
202         // Exercise is part of test toggle with monthly test and operator defined. some carriers
203         // mandate to show test settings in UI but always enable exercise alert.
204         setCellBroadcastRange(subId, enableTestAlerts ||
205                         res.getBoolean(R.bool.always_enable_exercise_alert),
206                 channelManager.getCellBroadcastChannelRanges(
207                         R.array.exercise_alert_range_strings));
208 
209         setCellBroadcastRange(subId, enableTestAlerts,
210                 channelManager.getCellBroadcastChannelRanges(
211                         R.array.operator_defined_alert_range_strings));
212 
213         // Enable/Disable GSM ETWS messages.
214         setCellBroadcastRange(subId, enableEtwsAlerts,
215                 channelManager.getCellBroadcastChannelRanges(
216                         R.array.etws_alerts_range_strings));
217 
218         // Enable/Disable GSM ETWS test messages.
219         setCellBroadcastRange(subId, enableTestAlerts,
220                 channelManager.getCellBroadcastChannelRanges(
221                         R.array.etws_test_alerts_range_strings));
222 
223         // Enable/Disable GSM public safety messages.
224         setCellBroadcastRange(subId, enablePublicSafetyMessagesChannelAlerts,
225                 channelManager.getCellBroadcastChannelRanges(
226                         R.array.public_safety_messages_channels_range_strings));
227 
228         // Enable/Disable GSM state/local test alerts.
229         setCellBroadcastRange(subId, enableStateLocalTestAlerts,
230                 channelManager.getCellBroadcastChannelRanges(
231                         R.array.state_local_test_alert_range_strings));
232 
233         // Enable/Disable GSM geo-fencing trigger messages.
234         setCellBroadcastRange(subId, enableGeoFencingTriggerMessage,
235                 channelManager.getCellBroadcastChannelRanges(
236                         R.array.geo_fencing_trigger_messages_range_strings));
237 
238         // Enable non-CMAS series messages.
239         setCellBroadcastRange(subId, enableEmergencyAlerts,
240                 channelManager.getCellBroadcastChannelRanges(
241                         R.array.emergency_alerts_channels_range_strings));
242 
243         // Enable/Disable additional channels based on carrier specific requirement.
244         ArrayList<CellBroadcastChannelRange> ranges =
245                 channelManager.getCellBroadcastChannelRanges(
246                         R.array.additional_cbs_channels_strings);
247 
248         for (CellBroadcastChannelRange range: ranges) {
249             boolean enableAlerts;
250             switch (range.mAlertType) {
251                 case AREA:
252                     enableAlerts = enableAreaUpdateInfoAlerts;
253                     break;
254                 case TEST:
255                     enableAlerts = enableTestAlerts;
256                     break;
257                 default:
258                     enableAlerts = enableAlertsMasterToggle;
259             }
260             setCellBroadcastRange(subId, enableAlerts, new ArrayList<>(Arrays.asList(range)));
261         }
262     }
263     /**
264      * Enable/disable cell broadcast with messages id range
265      * @param subId Subscription index
266      * @param enable True for enabling cell broadcast with id range, otherwise for disabling.
267      * @param ranges Cell broadcast id ranges
268      */
setCellBroadcastRange(int subId, boolean enable, List<CellBroadcastChannelRange> ranges)269     private void setCellBroadcastRange(int subId, boolean enable,
270                                        List<CellBroadcastChannelRange> ranges) {
271         SmsManager manager;
272         if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
273             manager = SmsManager.getSmsManagerForSubscriptionId(subId);
274         } else {
275             manager = SmsManager.getDefault();
276         }
277 
278         if (ranges != null) {
279             for (CellBroadcastChannelRange range: ranges) {
280                 if (enable) {
281                     manager.enableCellBroadcastRange(range.mStartId, range.mEndId, range.mRanType);
282                 } else {
283                     manager.disableCellBroadcastRange(range.mStartId, range.mEndId, range.mRanType);
284                 }
285             }
286         }
287     }
288 
log(String msg)289     private static void log(String msg) {
290         Log.d(TAG, msg);
291     }
292 }
293