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