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 import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERRSRC_CBR;
21 import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERRTYPE_CHANNEL_R;
22 import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERRTYPE_ENABLECHANNEL;
23 
24 import android.Manifest;
25 import android.app.ActivityOptions;
26 import android.app.IntentService;
27 import android.app.Notification;
28 import android.app.NotificationManager;
29 import android.app.PendingIntent;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.SharedPreferences;
33 import android.content.res.Resources;
34 import android.preference.PreferenceManager;
35 import android.telephony.CellBroadcastIdRange;
36 import android.telephony.SmsManager;
37 import android.telephony.SubscriptionInfo;
38 import android.telephony.SubscriptionManager;
39 import android.telephony.TelephonyManager;
40 import android.text.TextUtils;
41 import android.util.Log;
42 import android.util.Pair;
43 
44 import androidx.annotation.NonNull;
45 
46 import com.android.cellbroadcastreceiver.CellBroadcastChannelManager.CellBroadcastChannelRange;
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.modules.utils.build.SdkLevel;
49 
50 import java.lang.reflect.Method;
51 import java.util.ArrayList;
52 import java.util.HashSet;
53 import java.util.List;
54 
55 /**
56  * This service manages enabling and disabling ranges of message identifiers
57  * that the radio should listen for. It operates independently of the other
58  * services and runs at boot time and after exiting airplane mode.
59  *
60  * Note that the entire range of emergency channels is enabled. Test messages
61  * and lower priority broadcasts are filtered out in CellBroadcastAlertService
62  * if the user has not enabled them in settings.
63  *
64  * TODO: add notification to re-enable channels after a radio reset.
65  */
66 public class CellBroadcastConfigService extends IntentService {
67     private static final String TAG = "CellBroadcastConfigService";
68 
69     private HashSet<Pair<Integer, Integer>> mChannelRangeForMetric = new HashSet<>();
70 
71     @VisibleForTesting
72     public static final String ACTION_ENABLE_CHANNELS = "ACTION_ENABLE_CHANNELS";
73     public static final String ACTION_UPDATE_SETTINGS_FOR_CARRIER = "UPDATE_SETTINGS_FOR_CARRIER";
74     public static final String ACTION_RESET_SETTINGS_AS_NEEDED = "RESET_SETTINGS_AS_NEEDED";
75 
76     public static final String EXTRA_SUB = "SUB";
77 
78     private static final String ACTION_SET_CHANNELS_DONE =
79             "android.cellbroadcast.compliancetest.SET_CHANNELS_DONE";
80     /**
81      * CbConfig is consisted by starting channel id, ending channel id, and ran type,
82      * whether it should be enabled or not
83      */
84     public static class CbConfig {
85         public int mStartId;
86         public int mEndId;
87         public int mRanType;
88         public boolean mEnable;
89 
CbConfig(int startId, int endId, int type, boolean enable)90         public CbConfig(int startId, int endId, int type, boolean enable) {
91             this.mStartId = startId;
92             this.mEndId = endId;
93             this.mRanType = type;
94             this.mEnable = enable;
95         }
96     }
97 
CellBroadcastConfigService()98     public CellBroadcastConfigService() {
99         super(TAG);          // use class name for worker thread name
100     }
101 
102     @Override
onHandleIntent(Intent intent)103     protected void onHandleIntent(Intent intent) {
104         if (ACTION_ENABLE_CHANNELS.equals(intent.getAction())) {
105             try {
106                 SubscriptionManager subManager = (SubscriptionManager) getApplicationContext()
107                         .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
108 
109                 if (subManager != null) {
110                     mChannelRangeForMetric.clear();
111                     // Retrieve all the active subscription inside and enable cell broadcast
112                     // messages on all subs. The duplication detection will be done at the
113                     // frameworks.
114                     int[] subIds = getActiveSubIdList(subManager);
115                     if (subIds.length != 0) {
116                         for (int subId : subIds) {
117                             log("Enable CellBroadcast on sub " + subId);
118                             enableCellBroadcastChannels(subId);
119                             if (!SdkLevel.isAtLeastU()) {
120                                 broadcastSetChannelsIsDone(subId);
121                             }
122                         }
123                     } else {
124                         // For no sim scenario.
125                         enableCellBroadcastChannels(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
126                     }
127 
128                     if (!mChannelRangeForMetric.isEmpty()) {
129                         String roamingOperator = CellBroadcastReceiver.getRoamingOperatorSupported(
130                                 this);
131                         CellBroadcastReceiverMetrics.getInstance().onConfigUpdated(
132                                 getApplicationContext(),
133                                 roamingOperator.isEmpty() ? "" : roamingOperator,
134                                 mChannelRangeForMetric);
135                     }
136                 }
137             } catch (Exception ex) {
138                 CellBroadcastReceiverMetrics.getInstance().logModuleError(
139                         ERRSRC_CBR, ERRTYPE_ENABLECHANNEL);
140                 Log.e(TAG, "exception enabling cell broadcast channels", ex);
141             }
142         } else if (ACTION_UPDATE_SETTINGS_FOR_CARRIER.equals(intent.getAction())) {
143             Context c = getApplicationContext();
144             if (CellBroadcastSettings.hasAnyPreferenceChanged(c)) {
145                 Log.d(TAG, "Preference has changed from user set, posting notification.");
146 
147                 CellBroadcastAlertService.createNotificationChannels(c);
148                 Intent settingsIntent = new Intent(c, CellBroadcastSettings.class);
149                 ActivityOptions options = ActivityOptions.makeBasic();
150                 if (SdkLevel.isAtLeastU()) {
151                     options.setPendingIntentCreatorBackgroundActivityStartMode(
152                             ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
153                 }
154                 PendingIntent pi = PendingIntent.getActivity(c,
155                         CellBroadcastAlertService.SETTINGS_CHANGED_NOTIFICATION_ID, settingsIntent,
156                         PendingIntent.FLAG_ONE_SHOT
157                                 | PendingIntent.FLAG_UPDATE_CURRENT
158                                 | PendingIntent.FLAG_IMMUTABLE, options.toBundle());
159 
160                 Notification.Builder builder = new Notification.Builder(c,
161                         CellBroadcastAlertService.NOTIFICATION_CHANNEL_SETTINGS_UPDATES)
162                         .setCategory(Notification.CATEGORY_SYSTEM)
163                         .setContentTitle(c.getString(R.string.notification_cb_settings_changed_title))
164                         .setContentText(c.getString(R.string.notification_cb_settings_changed_text))
165                         .setSmallIcon(R.drawable.ic_settings_gear_outline_24dp)
166                         .setContentIntent(pi)
167                         .setAutoCancel(true);
168                 NotificationManager notificationManager = c.getSystemService(
169                         NotificationManager.class);
170                 notificationManager.notify(
171                         CellBroadcastAlertService.SETTINGS_CHANGED_NOTIFICATION_ID,
172                         builder.build());
173             }
174             Log.e(TAG, "Reset all preferences");
175             resetAllPreferences();
176         } else if (ACTION_RESET_SETTINGS_AS_NEEDED.equals(intent.getAction())) {
177             Resources res = getResources(intent.getIntExtra(
178                     EXTRA_SUB, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID), null);
179 
180             /// TODO :: b/339644128 - Reset WEA preferences when user has not modified them
181             if (!CellBroadcastSettings.hasAnyPreferenceChanged(getApplicationContext())) {
182                 if (isMasterToggleEnabled() != res.getBoolean(R.bool.master_toggle_enabled_default)
183                         || (isSpeechAlertMessageEnabled() != res.getBoolean(
184                         R.bool.enable_alert_speech_default))) {
185                     Log.d(TAG, "Reset all preferences as no user changes and "
186                             + "master toggle is different as the config or "
187                             + "alert speech toggle is different as the config");
188                     resetAllPreferences();
189                 }
190             }
191         }
192     }
193 
194     /**
195      * Encapsulate the static method to reset all preferences for testing purpose.
196      */
197     @VisibleForTesting
resetAllPreferences()198     public void resetAllPreferences() {
199         CellBroadcastSettings.resetAllPreferences(getApplicationContext());
200     }
201 
202     @NonNull
getActiveSubIdList(SubscriptionManager subMgr)203     private int[] getActiveSubIdList(SubscriptionManager subMgr) {
204         List<SubscriptionInfo> subInfos = subMgr.getActiveSubscriptionInfoList();
205         int size = subInfos != null ? subInfos.size() : 0;
206         int[] subIds = new int[size];
207         for (int i = 0; i < size; i++) {
208             subIds[i] = subInfos.get(i).getSubscriptionId();
209         }
210         return subIds;
211     }
212 
213     /**
214      * reset cell broadcast ranges
215      */
216     @VisibleForTesting
resetCellBroadcastChannels(int subId)217     public void resetCellBroadcastChannels(int subId) {
218         if (SdkLevel.isAtLeastU()) {
219             return;
220         }
221         SmsManager manager;
222         if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
223             manager = SmsManager.getSmsManagerForSubscriptionId(subId);
224         } else {
225             manager = SmsManager.getDefault();
226         }
227         // SmsManager.resetAllCellBroadcastRanges is a new @SystemAPI in S. We need to support
228         // backward compatibility as the module need to run on R build as well.
229         if (SdkLevel.isAtLeastS()) {
230             manager.resetAllCellBroadcastRanges();
231         } else {
232             try {
233                 Method method = SmsManager.class.getDeclaredMethod("resetAllCellBroadcastRanges");
234                 method.invoke(manager);
235             } catch (Exception e) {
236                 CellBroadcastReceiverMetrics.getInstance().logModuleError(
237                         ERRSRC_CBR, ERRTYPE_CHANNEL_R);
238                 log("Can't reset cell broadcast ranges. e=" + e);
239             }
240         }
241     }
242 
243     /**
244      * Enable cell broadcast messages channels. Messages can be only received on the
245      * enabled channels.
246      *
247      * @param subId Subscription index
248      */
249     @VisibleForTesting
enableCellBroadcastChannels(int subId)250     public void enableCellBroadcastChannels(int subId) {
251         resetCellBroadcastChannels(subId);
252 
253         List<CbConfig> config = getCellBroadcastChannelsConfig(subId, null);
254 
255         String roamingOperator = CellBroadcastReceiver.getRoamingOperatorSupported(this);
256         if (!TextUtils.isEmpty(roamingOperator)) {
257             config.addAll(getCellBroadcastChannelsConfig(subId, roamingOperator));
258             config = mergeConfigAsNeeded(config);
259         }
260         setCellBroadcastRange(subId, config);
261     }
262 
getCellBroadcastChannelsConfig(int subId, String roamingOperator)263     private List<CbConfig> getCellBroadcastChannelsConfig(int subId, String roamingOperator) {
264 
265         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
266         Resources res = getResources(subId, roamingOperator);
267         boolean isRoaming = !TextUtils.isEmpty(roamingOperator);
268 
269         // boolean for each user preference checkbox, true for checked, false for unchecked
270         // Note: If enableAlertsMasterToggle is false, it disables ALL emergency broadcasts
271         // except for always-on alerts e.g, presidential. i.e. to receive CMAS severe alerts, both
272         // enableAlertsMasterToggle AND enableCmasSevereAlerts must be true.
273         boolean enableAlertsMasterToggle = isMasterToggleEnabled();
274 
275         boolean enableEtwsAlerts = enableAlertsMasterToggle;
276 
277         // CMAS Presidential must be always on (See 3GPP TS 22.268 Section 6.2) regardless
278         // user's preference
279         boolean enablePresidential = true;
280 
281         boolean enableCmasExtremeAlerts = enableAlertsMasterToggle && (isRoaming
282                 ? res.getBoolean(R.bool.extreme_threat_alerts_enabled_default)
283                 : prefs.getBoolean(
284                         CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS, true));
285 
286         boolean enableCmasSevereAlerts = enableAlertsMasterToggle && (isRoaming
287                 ? res.getBoolean(R.bool.severe_threat_alerts_enabled_default)
288                 : prefs.getBoolean(
289                         CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS, true));
290 
291         boolean enableCmasAmberAlerts = enableAlertsMasterToggle && (isRoaming
292                 ? res.getBoolean(R.bool.amber_alerts_enabled_default)
293                 : prefs.getBoolean(
294                         CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS, true));
295 
296         boolean enableTestAlerts = enableAlertsMasterToggle && (isRoaming
297                 ? (CellBroadcastSettings
298                         .isTestAlertsToggleVisible(getApplicationContext(), roamingOperator)
299                         && res.getBoolean(R.bool.test_alerts_enabled_default))
300                 : (CellBroadcastSettings.isTestAlertsToggleVisible(getApplicationContext())
301                         && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_TEST_ALERTS,
302                         false)));
303 
304         boolean enableExerciseAlerts = enableAlertsMasterToggle && (isRoaming
305                 ? (res.getBoolean(R.bool.show_separate_exercise_settings)
306                 && res.getBoolean(R.bool.test_exercise_alerts_enabled_default))
307                 : (res.getBoolean(R.bool.show_separate_exercise_settings)
308                         && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_EXERCISE_ALERTS,
309                         false)));
310 
311         boolean enableOperatorDefined = enableAlertsMasterToggle && (isRoaming
312                 ? (res.getBoolean(R.bool.show_separate_operator_defined_settings)
313                 && res.getBoolean(R.bool.test_operator_defined_alerts_enabled_default))
314                 : (res.getBoolean(R.bool.show_separate_operator_defined_settings)
315                         && prefs.getBoolean(CellBroadcastSettings.KEY_OPERATOR_DEFINED_ALERTS,
316                         false)));
317 
318         boolean enableAreaUpdateInfoAlerts = isRoaming
319                 ? (res.getBoolean(R.bool.config_showAreaUpdateInfoSettings)
320                 && res.getBoolean(R.bool.area_update_info_alerts_enabled_default))
321                 : (res.getBoolean(R.bool.config_showAreaUpdateInfoSettings)
322                         && prefs.getBoolean(
323                                 CellBroadcastSettings.KEY_ENABLE_AREA_UPDATE_INFO_ALERTS, false));
324 
325         boolean enablePublicSafetyMessagesChannelAlerts = enableAlertsMasterToggle && (isRoaming
326                 ? res.getBoolean(R.bool.public_safety_messages_enabled_default)
327                 : prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_PUBLIC_SAFETY_MESSAGES, true));
328 
329         boolean enableStateLocalTestAlerts = enableAlertsMasterToggle && (isRoaming
330                 ? res.getBoolean(R.bool.state_local_test_alerts_enabled_default)
331                 : (prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_STATE_LOCAL_TEST_ALERTS,
332                         false) || (!res.getBoolean(R.bool.show_state_local_test_settings)
333                         && res.getBoolean(R.bool.state_local_test_alerts_enabled_default))));
334 
335         boolean enableEmergencyAlerts = enableAlertsMasterToggle && (isRoaming
336                 ? res.getBoolean(R.bool.emergency_alerts_enabled_default)
337                 : prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_EMERGENCY_ALERTS, true));
338 
339         return getCellBroadcastChannelsConfig(subId, roamingOperator, enableAlertsMasterToggle,
340                 enableEtwsAlerts, enablePresidential, enableCmasExtremeAlerts,
341                 enableCmasSevereAlerts, enableCmasAmberAlerts, enableTestAlerts,
342                 enableExerciseAlerts, enableOperatorDefined, enableAreaUpdateInfoAlerts,
343                 enablePublicSafetyMessagesChannelAlerts, enableStateLocalTestAlerts,
344                 enableEmergencyAlerts, true);
345     }
346 
getCellBroadcastChannelsConfig(int subId, @NonNull String operator, boolean enableAlertsMasterToggle, boolean enableEtwsAlerts, boolean enablePresidential, boolean enableCmasExtremeAlerts, boolean enableCmasSevereAlerts, boolean enableCmasAmberAlerts, boolean enableTestAlerts, boolean enableExerciseAlerts, boolean enableOperatorDefined, boolean enableAreaUpdateInfoAlerts, boolean enablePublicSafetyMessagesChannelAlerts, boolean enableStateLocalTestAlerts, boolean enableEmergencyAlerts, boolean enableGeoFencingTriggerMessage)347     private List<CbConfig> getCellBroadcastChannelsConfig(int subId, @NonNull String operator,
348             boolean enableAlertsMasterToggle, boolean enableEtwsAlerts, boolean enablePresidential,
349             boolean enableCmasExtremeAlerts, boolean enableCmasSevereAlerts,
350             boolean enableCmasAmberAlerts, boolean enableTestAlerts, boolean enableExerciseAlerts,
351             boolean enableOperatorDefined, boolean enableAreaUpdateInfoAlerts,
352             boolean enablePublicSafetyMessagesChannelAlerts, boolean enableStateLocalTestAlerts,
353             boolean enableEmergencyAlerts, boolean enableGeoFencingTriggerMessage) {
354 
355         if (VDBG) {
356             log("setCellBroadcastChannelsEnabled for " + subId + ", operator: " + operator);
357             log("enableAlertsMasterToggle = " + enableAlertsMasterToggle);
358             log("enableEtwsAlerts = " + enableEtwsAlerts);
359             log("enablePresidential = " + enablePresidential);
360             log("enableCmasExtremeAlerts = " + enableCmasExtremeAlerts);
361             log("enableCmasSevereAlerts = " + enableCmasSevereAlerts);
362             log("enableCmasAmberAlerts = " + enableCmasAmberAlerts);
363             log("enableTestAlerts = " + enableTestAlerts);
364             log("enableExerciseAlerts = " + enableExerciseAlerts);
365             log("enableOperatorDefinedAlerts = " + enableOperatorDefined);
366             log("enableAreaUpdateInfoAlerts = " + enableAreaUpdateInfoAlerts);
367             log("enablePublicSafetyMessagesChannelAlerts = "
368                     + enablePublicSafetyMessagesChannelAlerts);
369             log("enableStateLocalTestAlerts = " + enableStateLocalTestAlerts);
370             log("enableEmergencyAlerts = " + enableEmergencyAlerts);
371             log("enableGeoFencingTriggerMessage = " + enableGeoFencingTriggerMessage);
372         }
373 
374         List<CbConfig> cbConfigList = new ArrayList<>();
375         boolean isEnableOnly = !TextUtils.isEmpty(operator);
376         CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager(
377                 getApplicationContext(), subId, operator);
378         /** Enable CMAS series messages. */
379 
380         // Enable/Disable Presidential messages.
381         List<CellBroadcastChannelRange> ranges = channelManager.getCellBroadcastChannelRanges(
382                 R.array.cmas_presidential_alerts_channels_range_strings);
383         for (CellBroadcastChannelRange range : ranges) {
384             boolean enable = range.mAlwaysOn || enablePresidential;
385             if (enable || !isEnableOnly) {
386                 cbConfigList.add(
387                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
388             }
389         }
390 
391         // Enable/Disable CMAS extreme messages.
392         ranges = channelManager.getCellBroadcastChannelRanges(
393                 R.array.cmas_alert_extreme_channels_range_strings);
394         for (CellBroadcastChannelRange range : ranges) {
395             boolean enable = range.mAlwaysOn || enableCmasExtremeAlerts;
396             if (enable || !isEnableOnly) {
397                 cbConfigList.add(
398                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
399             }
400         }
401 
402         // Enable/Disable CMAS severe messages.
403         ranges = channelManager.getCellBroadcastChannelRanges(
404                 R.array.cmas_alerts_severe_range_strings);
405         for (CellBroadcastChannelRange range : ranges) {
406             boolean enable = range.mAlwaysOn || enableCmasSevereAlerts;
407             if (enable || !isEnableOnly) {
408                 cbConfigList.add(
409                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
410             }
411         }
412 
413         // Enable/Disable CMAS amber alert messages.
414         ranges = channelManager.getCellBroadcastChannelRanges(
415                 R.array.cmas_amber_alerts_channels_range_strings);
416         for (CellBroadcastChannelRange range : ranges) {
417             boolean enable = range.mAlwaysOn || enableCmasAmberAlerts;
418             if (enable || !isEnableOnly) {
419                 cbConfigList.add(
420                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
421             }
422         }
423 
424         // Enable/Disable test messages.
425         ranges = channelManager.getCellBroadcastChannelRanges(
426                 R.array.required_monthly_test_range_strings);
427         for (CellBroadcastChannelRange range : ranges) {
428             boolean enable = range.mAlwaysOn || enableTestAlerts;
429             if (enable || !isEnableOnly) {
430                 cbConfigList.add(
431                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
432             }
433         }
434 
435         // Enable/Disable exercise test messages.
436         // This could either controlled by main test toggle or separate exercise test toggle.
437         ranges = channelManager.getCellBroadcastChannelRanges(R.array.exercise_alert_range_strings);
438         for (CellBroadcastChannelRange range : ranges) {
439             boolean enable = range.mAlwaysOn || (enableTestAlerts || enableExerciseAlerts);
440             if (enable || !isEnableOnly) {
441                 cbConfigList.add(
442                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
443             }
444         }
445 
446         // Enable/Disable operator defined test messages.
447         // This could either controlled by main test toggle or separate operator defined test toggle
448         ranges = channelManager.getCellBroadcastChannelRanges(
449                 R.array.operator_defined_alert_range_strings);
450         for (CellBroadcastChannelRange range : ranges) {
451             boolean enable = range.mAlwaysOn || (enableTestAlerts || enableOperatorDefined);
452             if (enable || !isEnableOnly) {
453                 cbConfigList.add(
454                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
455             }
456         }
457 
458         // Enable/Disable GSM ETWS messages.
459         ranges = channelManager.getCellBroadcastChannelRanges(R.array.etws_alerts_range_strings);
460         for (CellBroadcastChannelRange range : ranges) {
461             boolean enable = range.mAlwaysOn || enableEtwsAlerts;
462             if (enable || !isEnableOnly) {
463                 cbConfigList.add(
464                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
465             }
466         }
467 
468         // Enable/Disable GSM ETWS test messages.
469         ranges = channelManager.getCellBroadcastChannelRanges(
470                 R.array.etws_test_alerts_range_strings);
471         for (CellBroadcastChannelRange range : ranges) {
472             boolean enable = range.mAlwaysOn || enableTestAlerts;
473             if (enable || !isEnableOnly) {
474                 cbConfigList.add(
475                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
476             }
477         }
478 
479         // Enable/Disable GSM public safety messages.
480         ranges = channelManager.getCellBroadcastChannelRanges(
481                 R.array.public_safety_messages_channels_range_strings);
482         for (CellBroadcastChannelRange range : ranges) {
483             boolean enable = range.mAlwaysOn || enablePublicSafetyMessagesChannelAlerts;
484             if (enable || !isEnableOnly) {
485                 cbConfigList.add(
486                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
487             }
488         }
489 
490         // Enable/Disable GSM state/local test alerts.
491         ranges = channelManager.getCellBroadcastChannelRanges(
492                 R.array.state_local_test_alert_range_strings);
493         for (CellBroadcastChannelRange range : ranges) {
494             boolean enable = range.mAlwaysOn || enableStateLocalTestAlerts;
495             if (enable || !isEnableOnly) {
496                 cbConfigList.add(
497                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
498             }
499         }
500 
501         // Enable/Disable GSM geo-fencing trigger messages.
502         ranges = channelManager.getCellBroadcastChannelRanges(
503                 R.array.geo_fencing_trigger_messages_range_strings);
504         for (CellBroadcastChannelRange range : ranges) {
505             boolean enable = range.mAlwaysOn || enableGeoFencingTriggerMessage;
506             if (enable || !isEnableOnly) {
507                 cbConfigList.add(
508                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
509             }
510         }
511 
512         // Enable non-CMAS series messages.
513         ranges = channelManager.getCellBroadcastChannelRanges(
514                 R.array.emergency_alerts_channels_range_strings);
515         for (CellBroadcastChannelRange range : ranges) {
516             boolean enable = range.mAlwaysOn || enableEmergencyAlerts;
517             if (enable || !isEnableOnly) {
518                 cbConfigList.add(
519                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
520             }
521         }
522 
523         // Enable/Disable additional channels based on carrier specific requirement.
524         List<CellBroadcastChannelRange> additionChannelRanges =
525                 channelManager.getCellBroadcastChannelRanges(
526                         R.array.additional_cbs_channels_strings);
527 
528         for (CellBroadcastChannelRange range : additionChannelRanges) {
529             boolean enableAlerts;
530             switch (range.mAlertType) {
531                 case AREA:
532                     enableAlerts = enableAreaUpdateInfoAlerts;
533                     break;
534                 case TEST:
535                     enableAlerts = enableTestAlerts;
536                     break;
537                 default:
538                     enableAlerts = enableAlertsMasterToggle;
539             }
540             boolean enable = range.mAlwaysOn || enableAlerts;
541             if (enable || !isEnableOnly) {
542                 cbConfigList.add(
543                         new CbConfig(range.mStartId, range.mEndId, range.mRanType, enable));
544             }
545         }
546         return cbConfigList;
547     }
548 
549     /**
550      * Enable/disable cell broadcast with messages id range
551      *
552      * @param subId         Subscription index
553      * @param ranges        Cell broadcast id ranges
554      */
setCellBroadcastRange(int subId, List<CbConfig> ranges)555     private void setCellBroadcastRange(int subId, List<CbConfig> ranges) {
556         SmsManager manager;
557         if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
558             manager = SmsManager.getSmsManagerForSubscriptionId(subId);
559         } else {
560             manager = SmsManager.getDefault();
561         }
562         List<CellBroadcastIdRange> channelIdRanges = new ArrayList<>();
563 
564         if (ranges != null) {
565             for (CbConfig range : ranges) {
566                 boolean enable = range.mEnable;
567                 if (SdkLevel.isAtLeastU()) {
568                     if (VDBG) {
569                         log("enableCellBroadcastRange[" + range.mStartId + "-"
570                                 + range.mEndId + "], type:" + range.mRanType
571                                 + ", enable:" + enable);
572                     }
573                     if (enable && (subId == SubscriptionManager.getDefaultSubscriptionId())) {
574                         mChannelRangeForMetric.add(new Pair(range.mStartId, range.mEndId));
575                     }
576                     CellBroadcastIdRange cbRange = new CellBroadcastIdRange(range.mStartId,
577                             range.mEndId, range.mRanType, enable);
578                     channelIdRanges.add(cbRange);
579                 } else {
580                     if (VDBG) {
581                         log("enableCellBroadcastRange[" + range.mStartId + "-"
582                                 + range.mEndId + "], type:" + range.mRanType);
583                     }
584                     if (enable) {
585                         if (subId == SubscriptionManager.getDefaultSubscriptionId()) {
586                             mChannelRangeForMetric.add(new Pair(range.mStartId, range.mEndId));
587                         }
588                         manager.enableCellBroadcastRange(range.mStartId, range.mEndId,
589                                 range.mRanType);
590                     } else {
591                         if (VDBG) {
592                             log("disableCellBroadcastRange[" + range.mStartId + "-"
593                                     + range.mEndId + "], type:" + range.mRanType);
594                         }
595                         manager.disableCellBroadcastRange(range.mStartId, range.mEndId,
596                                 range.mRanType);
597                     }
598                 }
599             }
600             if (SdkLevel.isAtLeastU()) {
601                 TelephonyManager tm = getApplicationContext().getSystemService(
602                         TelephonyManager.class).createForSubscriptionId(subId);
603                 try {
604                     tm.setCellBroadcastIdRanges(channelIdRanges, Runnable::run,  result -> {
605                         if (result != TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS) {
606                             Log.e(TAG, "fails to setCellBroadcastRanges, result = " + result);
607                         }
608                     });
609                 } catch (RuntimeException e) {
610                     Log.e(TAG, "fails to setCellBroadcastRanges");
611                 }
612             }
613         }
614     }
615 
616     /**
617      * Merge the conflicted CbConfig in the list as needed
618      * @param inputRanges input config lists
619      * @return the list of CbConfig without conflict
620      */
621     @VisibleForTesting
mergeConfigAsNeeded(List<CbConfig> inputRanges)622     public static List<CbConfig> mergeConfigAsNeeded(List<CbConfig> inputRanges) {
623         inputRanges.sort((r1, r2) -> r1.mRanType != r2.mRanType ? r1.mRanType - r2.mRanType
624                 : (r1.mStartId != r2.mStartId ? r1.mStartId - r2.mStartId
625                         : r2.mEndId - r1.mEndId));
626         final List<CbConfig> ranges = new ArrayList<>();
627         inputRanges.forEach(r -> {
628             if (ranges.isEmpty() || ranges.get(ranges.size() - 1).mRanType != r.mRanType
629                     || ranges.get(ranges.size() - 1).mEndId < r.mStartId) {
630                 ranges.add(new CbConfig(r.mStartId, r.mEndId, r.mRanType, r.mEnable));
631             } else {
632                 CbConfig range = ranges.get(ranges.size() - 1);
633                 if (range.mEnable == r.mEnable) {
634                     if (r.mEndId > range.mEndId) {
635                         ranges.set(ranges.size() - 1, new CbConfig(
636                                 range.mStartId, r.mEndId, range.mRanType, range.mEnable));
637                     }
638                 } else if (!range.mEnable) {
639                     if (range.mStartId < r.mStartId) {
640                         if (range.mEndId <= r.mEndId) {
641                             ranges.set(ranges.size() - 1, new CbConfig(range.mStartId,
642                                     r.mStartId - 1, range.mRanType, false));
643                             ranges.add(new CbConfig(r.mStartId, r.mEndId, r.mRanType, true));
644                         } else {
645                             ranges.set(ranges.size() - 1, new CbConfig(range.mStartId,
646                                     r.mStartId - 1, range.mRanType, false));
647                             ranges.add(new CbConfig(r.mStartId, r.mEndId, r.mRanType, true));
648                             ranges.add(new CbConfig(r.mEndId + 1, range.mEndId,
649                                     range.mRanType, false));
650                         }
651                     } else {
652                         if (range.mEndId <= r.mEndId) {
653                             ranges.set(ranges.size() - 1, new CbConfig(r.mStartId,
654                                     r.mEndId, range.mRanType, true));
655                         } else if (range.mStartId <= r.mEndId) {
656                             ranges.set(ranges.size() - 1, new CbConfig(r.mStartId,
657                                     r.mEndId, range.mRanType, true));
658                             ranges.add(new CbConfig(r.mEndId + 1, range.mEndId,
659                                     r.mRanType, false));
660                         }
661                     }
662                 } else {
663                     if (range.mEndId < r.mEndId) {
664                         ranges.add(new CbConfig(range.mEndId + 1, r.mEndId, r.mRanType, false));
665                     }
666                 }
667             }
668         });
669         return ranges;
670     }
671 
672     /**
673      * Get resource according to the operator or subId
674      *
675      * @param subId    Subscription index
676      * @param operator Operator numeric, the resource will be retrieved by it if it is no null,
677      *                 otherwise, by the sub id.
678      */
679     @VisibleForTesting
getResources(int subId, String operator)680     public Resources getResources(int subId, String operator) {
681         if (operator == null) {
682             return CellBroadcastSettings.getResources(this, subId);
683         }
684         return CellBroadcastSettings.getResourcesByOperator(this, subId, operator);
685     }
686 
broadcastSetChannelsIsDone(int subId)687     private void broadcastSetChannelsIsDone(int subId) {
688         if (!isMockModemRunning()) {
689             return;
690         }
691         Intent intent = new Intent(ACTION_SET_CHANNELS_DONE);
692         intent.putExtra("sub_id", subId);
693         sendBroadcast(intent, Manifest.permission.READ_CELL_BROADCASTS);
694         Log.d(TAG, "broadcastSetChannelsIsDone subId = " + subId);
695     }
696 
isMasterToggleEnabled()697     private boolean isMasterToggleEnabled() {
698         return PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
699                 CellBroadcastSettings.KEY_ENABLE_ALERTS_MASTER_TOGGLE, true);
700     }
701 
isSpeechAlertMessageEnabled()702     private boolean isSpeechAlertMessageEnabled() {
703         return PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
704                 CellBroadcastSettings.KEY_ENABLE_ALERT_SPEECH, true);
705     }
706 
707     /**
708      * Check if mockmodem is running
709      * @return true if mockmodem service is running instead of real modem
710      */
711     @VisibleForTesting
isMockModemRunning()712     public boolean isMockModemRunning() {
713         return CellBroadcastReceiver.isMockModemBinded();
714     }
715 
log(String msg)716     private static void log(String msg) {
717         Log.d(TAG, msg);
718     }
719 }
720