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