• 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"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.cellbroadcastreceiver;
18 
19 import android.annotation.NonNull;
20 import android.app.ActionBar;
21 import android.app.ActivityManager;
22 import android.app.Fragment;
23 import android.app.backup.BackupManager;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.SharedPreferences;
29 import android.content.pm.PackageManager;
30 import android.content.res.Resources;
31 import android.os.Bundle;
32 import android.os.PersistableBundle;
33 import android.os.UserManager;
34 import android.os.Vibrator;
35 import android.telephony.CarrierConfigManager;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38 import android.util.Log;
39 import android.view.MenuItem;
40 import android.widget.Switch;
41 
42 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
43 import androidx.preference.ListPreference;
44 import androidx.preference.Preference;
45 import androidx.preference.PreferenceCategory;
46 import androidx.preference.PreferenceFragment;
47 import androidx.preference.PreferenceManager;
48 import androidx.preference.PreferenceScreen;
49 import androidx.preference.TwoStatePreference;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.modules.utils.build.SdkLevel;
53 import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
54 import com.android.settingslib.widget.MainSwitchPreference;
55 import com.android.settingslib.widget.OnMainSwitchChangeListener;
56 
57 import java.util.HashMap;
58 import java.util.Map;
59 
60 /**
61  * Settings activity for the cell broadcast receiver.
62  */
63 public class CellBroadcastSettings extends CollapsingToolbarBaseActivity {
64 
65     private static final String TAG = "CellBroadcastSettings";
66 
67     private static final boolean DBG = false;
68 
69     /**
70      * Keys for user preferences.
71      * When adding a new preference, make sure to clear its value in resetAllPreferences.
72      */
73     // Preference key for alert header (A text view, not clickable).
74     public static final String KEY_ALERTS_HEADER = "alerts_header";
75 
76     // Preference key for a main toggle to enable/disable all alerts message (default enabled).
77     public static final String KEY_ENABLE_ALERTS_MASTER_TOGGLE = "enable_alerts_master_toggle";
78 
79     // Preference key for whether to enable public safety messages (default enabled).
80     public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES = "enable_public_safety_messages";
81 
82     // Preference key for whether to show full-screen public safety message (pop-up dialog), If set
83     // to false, only display from message history and sms inbox if enabled. A foreground
84     // notification might also be shown if enabled.
85     public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN =
86             "enable_public_safety_messages_full_screen";
87 
88     // Preference key for whether to enable emergency alerts (default enabled).
89     public static final String KEY_ENABLE_EMERGENCY_ALERTS = "enable_emergency_alerts";
90 
91     // Enable vibration on alert (unless main volume is silent).
92     public static final String KEY_ENABLE_ALERT_VIBRATE = "enable_alert_vibrate";
93 
94     // Speak contents of alert after playing the alert sound.
95     public static final String KEY_ENABLE_ALERT_SPEECH = "enable_alert_speech";
96 
97     // Play alert sound in full volume regardless Do Not Disturb is on.
98     public static final String KEY_OVERRIDE_DND = "override_dnd";
99 
100     public static final String KEY_OVERRIDE_DND_SETTINGS_CHANGED =
101             "override_dnd_settings_changed";
102 
103     // Preference category for emergency alert and CMAS settings.
104     public static final String KEY_CATEGORY_EMERGENCY_ALERTS = "category_emergency_alerts";
105 
106     // Preference category for alert preferences.
107     public static final String KEY_CATEGORY_ALERT_PREFERENCES = "category_alert_preferences";
108 
109     // Show checkbox for Presidential alerts in settings
110     // Whether to display CMAS presidential alert notifications (always enabled).
111     public static final String KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS =
112             "enable_cmas_presidential_alerts";
113 
114     // Whether to display CMAS extreme threat notifications (default is enabled).
115     public static final String KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS =
116             "enable_cmas_extreme_threat_alerts";
117 
118     // Whether to display CMAS severe threat notifications (default is enabled).
119     public static final String KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS =
120             "enable_cmas_severe_threat_alerts";
121 
122     // Whether to display CMAS amber alert messages (default is enabled).
123     public static final String KEY_ENABLE_CMAS_AMBER_ALERTS = "enable_cmas_amber_alerts";
124 
125     // Whether to display monthly test messages (default is disabled).
126     public static final String KEY_ENABLE_TEST_ALERTS = "enable_test_alerts";
127 
128     // Whether to display exercise test alerts.
129     public static final String KEY_ENABLE_EXERCISE_ALERTS = "enable_exercise_alerts";
130 
131     // Whether to display operator defined test alerts
132     public static final String KEY_OPERATOR_DEFINED_ALERTS = "enable_operator_defined_alerts";
133 
134     // Whether to display state/local test messages (default disabled).
135     public static final String KEY_ENABLE_STATE_LOCAL_TEST_ALERTS =
136             "enable_state_local_test_alerts";
137 
138     // Preference key for whether to enable area update information notifications
139     // Enabled by default for phones sold in Brazil and India, otherwise this setting may be hidden.
140     public static final String KEY_ENABLE_AREA_UPDATE_INFO_ALERTS =
141             "enable_area_update_info_alerts";
142 
143     // Preference key for initial opt-in/opt-out dialog.
144     public static final String KEY_SHOW_CMAS_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
145 
146     // Alert reminder interval ("once" = single 2 minute reminder).
147     public static final String KEY_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
148 
149     // Preference key for emergency alerts history
150     public static final String KEY_EMERGENCY_ALERT_HISTORY = "emergency_alert_history";
151 
152     // For watch layout
153     private static final String KEY_WATCH_ALERT_REMINDER = "watch_alert_reminder";
154 
155     // Whether to receive alert in second language code
156     public static final String KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE =
157             "receive_cmas_in_second_language";
158 
159     /* End of user preferences keys section. */
160 
161     // Resource cache
162     private static final Map<Integer, Resources> sResourcesCache = new HashMap<>();
163 
164     // Intent sent from cellbroadcastreceiver to notify cellbroadcastservice that area info update
165     // is disabled/enabled.
166     private static final String AREA_INFO_UPDATE_ACTION =
167             "com.android.cellbroadcastreceiver.action.AREA_UPDATE_INFO_ENABLED";
168     private static final String AREA_INFO_UPDATE_ENABLED_EXTRA = "enable";
169 
170     /**
171      * This permission is only granted to the cellbroadcast mainline module and thus can be
172      * used for permission check within CBR and CBS.
173      */
174     private static final String CBR_MODULE_PERMISSION =
175             "com.android.cellbroadcastservice.FULL_ACCESS_CELL_BROADCAST_HISTORY";
176 
177     // Key for shared preference which represents whether user has changed any preference
178     private static final String ANY_PREFERENCE_CHANGED_BY_USER = "any_preference_changed_by_user";
179 
180     // Test override for disabling the subId specific resources
181     private static boolean sUseResourcesForSubId = true;
182 
183     @Override
onCreate(Bundle savedInstanceState)184     public void onCreate(Bundle savedInstanceState) {
185         // for backward compatibility on R devices
186         if (!SdkLevel.isAtLeastS()) {
187             setCustomizeContentView(R.layout.cell_broadcast_list_collapsing_no_toobar);
188         }
189         super.onCreate(savedInstanceState);
190 
191         // for backward compatibility on R devices
192         if (!SdkLevel.isAtLeastS()) {
193             ActionBar actionBar = getActionBar();
194             if (actionBar != null) {
195                 // android.R.id.home will be triggered in onOptionsItemSelected()
196                 actionBar.setDisplayHomeAsUpEnabled(true);
197             }
198         }
199 
200         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
201         if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS)) {
202             setContentView(R.layout.cell_broadcast_disallowed_preference_screen);
203             return;
204         }
205 
206         // We only add new CellBroadcastSettingsFragment if no fragment is restored.
207         Fragment fragment = getFragmentManager().findFragmentById(
208                 com.android.settingslib.collapsingtoolbar.R.id.content_frame);
209         if (fragment == null) {
210             fragment = new CellBroadcastSettingsFragment();
211             getFragmentManager()
212                     .beginTransaction()
213                     .add(com.android.settingslib.collapsingtoolbar.R.id.content_frame, fragment)
214                     .commit();
215         }
216     }
217 
218     @Override
onStart()219     public void onStart() {
220         super.onStart();
221         getWindow().addSystemFlags(
222                 android.view.WindowManager.LayoutParams
223                         .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
224     }
225 
226     @Override
onOptionsItemSelected(MenuItem item)227     public boolean onOptionsItemSelected(MenuItem item) {
228         switch (item.getItemId()) {
229             // Respond to the action bar's Up/Home button
230             case android.R.id.home:
231                 finish();
232                 return true;
233         }
234         return super.onOptionsItemSelected(item);
235     }
236 
237     /**
238      * Reset all user values for preferences (stored in shared preferences).
239      *
240      * @param c the application context
241      */
resetAllPreferences(Context c)242     public static void resetAllPreferences(Context c) {
243         SharedPreferences.Editor e = PreferenceManager.getDefaultSharedPreferences(c).edit();
244         e.remove(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS)
245                 .remove(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS)
246                 .remove(KEY_ENABLE_CMAS_AMBER_ALERTS)
247                 .remove(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES)
248                 .remove(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN)
249                 .remove(KEY_ENABLE_EMERGENCY_ALERTS)
250                 .remove(KEY_ALERT_REMINDER_INTERVAL)
251                 .remove(KEY_ENABLE_ALERT_SPEECH)
252                 .remove(KEY_OVERRIDE_DND)
253                 .remove(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS)
254                 .remove(KEY_ENABLE_TEST_ALERTS)
255                 .remove(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS)
256                 .remove(KEY_ENABLE_ALERT_VIBRATE)
257                 .remove(KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS)
258                 .remove(KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE)
259                 .remove(KEY_ENABLE_EXERCISE_ALERTS)
260                 .remove(KEY_OPERATOR_DEFINED_ALERTS);
261         // If the device is in test harness mode, reset main toggle should only happen on the
262         // first boot.
263         if (!ActivityManager.isRunningInUserTestHarness()) {
264           Log.d(TAG, "In not test harness mode. reset main toggle.");
265           e.remove(KEY_ENABLE_ALERTS_MASTER_TOGGLE);
266         }
267         PackageManager pm = c.getPackageManager();
268         if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
269             e.remove(KEY_WATCH_ALERT_REMINDER);
270         }
271         e.commit();
272 
273         if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
274             PreferenceManager.setDefaultValues(c, R.xml.watch_preferences, true);
275         } else {
276             PreferenceManager.setDefaultValues(c, R.xml.preferences, true);
277         }
278         setPreferenceChanged(c, false);
279     }
280 
281     /**
282      * Return true if user has modified any preference manually.
283      * @param c the application context
284      * @return
285      */
hasAnyPreferenceChanged(Context c)286     public static boolean hasAnyPreferenceChanged(Context c) {
287         return PreferenceManager.getDefaultSharedPreferences(c)
288                 .getBoolean(ANY_PREFERENCE_CHANGED_BY_USER, false);
289     }
290 
setPreferenceChanged(Context c, boolean changed)291     private static void setPreferenceChanged(Context c, boolean changed) {
292         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
293         sp.edit().putBoolean(ANY_PREFERENCE_CHANGED_BY_USER, changed).apply();
294     }
295 
296     /**
297      * New fragment-style implementation of preferences.
298      */
299     public static class CellBroadcastSettingsFragment extends PreferenceFragment {
300 
301         private TwoStatePreference mExtremeCheckBox;
302         private TwoStatePreference mSevereCheckBox;
303         private TwoStatePreference mAmberCheckBox;
304         private MainSwitchPreference mMasterToggle;
305         private TwoStatePreference mPublicSafetyMessagesChannelCheckBox;
306         private TwoStatePreference mPublicSafetyMessagesChannelFullScreenCheckBox;
307         private TwoStatePreference mEmergencyAlertsCheckBox;
308         private ListPreference mReminderInterval;
309         private TwoStatePreference mSpeechCheckBox;
310         private TwoStatePreference mOverrideDndCheckBox;
311         private TwoStatePreference mAreaUpdateInfoCheckBox;
312         private TwoStatePreference mTestCheckBox;
313         private TwoStatePreference mExerciseTestCheckBox;
314         private TwoStatePreference mOperatorDefinedCheckBox;
315         private TwoStatePreference mStateLocalTestCheckBox;
316         private TwoStatePreference mEnableVibrateCheckBox;
317         private Preference mAlertHistory;
318         private Preference mAlertsHeader;
319         private PreferenceCategory mAlertCategory;
320         private PreferenceCategory mAlertPreferencesCategory;
321         private boolean mDisableSevereWhenExtremeDisabled = true;
322 
323         // WATCH
324         private TwoStatePreference mAlertReminder;
325 
326         // Show checkbox for Presidential alerts in settings
327         private TwoStatePreference mPresidentialCheckBox;
328 
329         // on/off switch in settings for receiving alert in second language code
330         private TwoStatePreference mReceiveCmasInSecondLanguageCheckBox;
331 
332         private final BroadcastReceiver mTestingModeChangedReceiver = new BroadcastReceiver() {
333             @Override
334             public void onReceive(Context context, Intent intent) {
335                 switch (intent.getAction()) {
336                     case CellBroadcastReceiver.ACTION_TESTING_MODE_CHANGED:
337                         updatePreferenceVisibility();
338                         break;
339                 }
340             }
341         };
342 
initPreferences()343         private void initPreferences() {
344             mExtremeCheckBox = (TwoStatePreference)
345                     findPreference(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS);
346             mSevereCheckBox = (TwoStatePreference)
347                     findPreference(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS);
348             mAmberCheckBox = (TwoStatePreference)
349                     findPreference(KEY_ENABLE_CMAS_AMBER_ALERTS);
350             mMasterToggle = (MainSwitchPreference)
351                     findPreference(KEY_ENABLE_ALERTS_MASTER_TOGGLE);
352             mPublicSafetyMessagesChannelCheckBox = (TwoStatePreference)
353                     findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES);
354             mPublicSafetyMessagesChannelFullScreenCheckBox = (TwoStatePreference)
355                     findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN);
356             mEmergencyAlertsCheckBox = (TwoStatePreference)
357                     findPreference(KEY_ENABLE_EMERGENCY_ALERTS);
358             mReminderInterval = (ListPreference)
359                     findPreference(KEY_ALERT_REMINDER_INTERVAL);
360             mSpeechCheckBox = (TwoStatePreference)
361                     findPreference(KEY_ENABLE_ALERT_SPEECH);
362             mOverrideDndCheckBox = (TwoStatePreference)
363                     findPreference(KEY_OVERRIDE_DND);
364             mAreaUpdateInfoCheckBox = (TwoStatePreference)
365                     findPreference(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS);
366             mTestCheckBox = (TwoStatePreference)
367                     findPreference(KEY_ENABLE_TEST_ALERTS);
368             mExerciseTestCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_EXERCISE_ALERTS);
369             mOperatorDefinedCheckBox = (TwoStatePreference)
370                     findPreference(KEY_OPERATOR_DEFINED_ALERTS);
371             mStateLocalTestCheckBox = (TwoStatePreference)
372                     findPreference(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS);
373             mAlertHistory = findPreference(KEY_EMERGENCY_ALERT_HISTORY);
374             mAlertsHeader = findPreference(KEY_ALERTS_HEADER);
375             mReceiveCmasInSecondLanguageCheckBox = (TwoStatePreference) findPreference
376                     (KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE);
377             mEnableVibrateCheckBox = findPreference(KEY_ENABLE_ALERT_VIBRATE);
378 
379             // Show checkbox for Presidential alerts in settings
380             mPresidentialCheckBox = (TwoStatePreference)
381                     findPreference(KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS);
382 
383             PackageManager pm = getActivity().getPackageManager();
384             if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
385                 mAlertReminder = (TwoStatePreference)
386                         findPreference(KEY_WATCH_ALERT_REMINDER);
387                 if (Integer.valueOf(mReminderInterval.getValue()) == 0) {
388                     mAlertReminder.setChecked(false);
389                 } else {
390                     mAlertReminder.setChecked(true);
391                 }
392                 mAlertReminder.setOnPreferenceChangeListener((p, newVal) -> {
393                     try {
394                         mReminderInterval.setValueIndex((Boolean) newVal ? 1 : 3);
395                     } catch (IndexOutOfBoundsException e) {
396                         mReminderInterval.setValue(String.valueOf(0));
397                         Log.w(TAG, "Setting default value");
398                     }
399                     return true;
400                 });
401                 PreferenceScreen watchScreen = (PreferenceScreen)
402                         findPreference(KEY_CATEGORY_ALERT_PREFERENCES);
403                 watchScreen.removePreference(mReminderInterval);
404             } else {
405                 mAlertPreferencesCategory = (PreferenceCategory)
406                         findPreference(KEY_CATEGORY_ALERT_PREFERENCES);
407                 mAlertCategory = (PreferenceCategory)
408                         findPreference(KEY_CATEGORY_EMERGENCY_ALERTS);
409             }
410         }
411 
412         @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)413         public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
414 
415             LocalBroadcastManager.getInstance(getContext())
416                     .registerReceiver(mTestingModeChangedReceiver, new IntentFilter(
417                             CellBroadcastReceiver.ACTION_TESTING_MODE_CHANGED));
418 
419             // Load the preferences from an XML resource
420             PackageManager pm = getActivity().getPackageManager();
421             if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
422                 addPreferencesFromResource(R.xml.watch_preferences);
423             } else {
424                 addPreferencesFromResource(R.xml.preferences);
425             }
426 
427             initPreferences();
428 
429             Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext());
430 
431             mDisableSevereWhenExtremeDisabled = res.getBoolean(
432                     R.bool.disable_severe_when_extreme_disabled);
433 
434             final OnMainSwitchChangeListener mainSwitchListener = new OnMainSwitchChangeListener() {
435                 @Override
436                 public void onSwitchChanged(Switch switchView, boolean isChecked) {
437                     setAlertsEnabled(isChecked);
438                 }
439             };
440 
441             // Handler for settings that require us to reconfigure enabled channels in radio
442             Preference.OnPreferenceChangeListener startConfigServiceListener =
443                     new Preference.OnPreferenceChangeListener() {
444                         @Override
445                         public boolean onPreferenceChange(Preference pref, Object newValue) {
446                             CellBroadcastReceiver.startConfigService(pref.getContext(),
447                                     CellBroadcastConfigService.ACTION_ENABLE_CHANNELS);
448                             setPreferenceChanged(getContext(), true);
449 
450                             if (mDisableSevereWhenExtremeDisabled) {
451                                 if (pref.getKey().equals(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS)) {
452                                     boolean isExtremeAlertChecked = (Boolean) newValue;
453                                     if (mSevereCheckBox != null) {
454                                         mSevereCheckBox.setEnabled(isExtremeAlertChecked);
455                                         mSevereCheckBox.setChecked(false);
456                                     }
457                                 }
458                             }
459 
460                             // check if area update was disabled
461                             if (pref.getKey().equals(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS)) {
462                                 boolean isEnabledAlert = (Boolean) newValue;
463                                 notifyAreaInfoUpdate(isEnabledAlert);
464                             }
465 
466                             // Notify backup manager a backup pass is needed.
467                             new BackupManager(getContext()).dataChanged();
468                             return true;
469                         }
470                     };
471 
472             initReminderIntervalList();
473 
474             if (mMasterToggle != null) {
475                 mMasterToggle.addOnSwitchChangeListener(mainSwitchListener);
476                 // If allow alerts are disabled, we turn all sub-alerts off. If it's enabled, we
477                 // leave them as they are.
478                 if (!mMasterToggle.isChecked()) {
479                     setAlertsEnabled(false);
480                 }
481             }
482             // note that mPresidentialCheckBox does not use the startConfigServiceListener because
483             // the user is never allowed to change the preference
484             if (mAreaUpdateInfoCheckBox != null) {
485                 mAreaUpdateInfoCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
486             }
487             if (mExtremeCheckBox != null) {
488                 mExtremeCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
489             }
490             if (mPublicSafetyMessagesChannelCheckBox != null) {
491                 mPublicSafetyMessagesChannelCheckBox.setOnPreferenceChangeListener(
492                         startConfigServiceListener);
493             }
494             if (mPublicSafetyMessagesChannelFullScreenCheckBox != null) {
495                 mPublicSafetyMessagesChannelFullScreenCheckBox.setOnPreferenceChangeListener(
496                         startConfigServiceListener);
497             }
498             if (mEmergencyAlertsCheckBox != null) {
499                 mEmergencyAlertsCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
500             }
501             if (mSevereCheckBox != null) {
502                 mSevereCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
503                 if (mDisableSevereWhenExtremeDisabled) {
504                     if (mExtremeCheckBox != null) {
505                         mSevereCheckBox.setEnabled(mExtremeCheckBox.isChecked());
506                     }
507                 }
508             }
509             if (mAmberCheckBox != null) {
510                 mAmberCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
511             }
512             if (mTestCheckBox != null) {
513                 mTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
514             }
515             if (mExerciseTestCheckBox != null) {
516                 mExerciseTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
517             }
518             if (mOperatorDefinedCheckBox != null) {
519                 mOperatorDefinedCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
520             }
521             if (mStateLocalTestCheckBox != null) {
522                 mStateLocalTestCheckBox.setOnPreferenceChangeListener(
523                         startConfigServiceListener);
524             }
525 
526             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
527 
528             if (mOverrideDndCheckBox != null) {
529                 if (!sp.getBoolean(KEY_OVERRIDE_DND_SETTINGS_CHANGED, false)) {
530                     // If the user hasn't changed this settings yet, use the default settings
531                     // from resource overlay.
532                     mOverrideDndCheckBox.setChecked(res.getBoolean(R.bool.override_dnd_default));
533                 }
534                 mOverrideDndCheckBox.setOnPreferenceChangeListener(
535                         (pref, newValue) -> {
536                             sp.edit().putBoolean(KEY_OVERRIDE_DND_SETTINGS_CHANGED,
537                                     true).apply();
538                             updateVibrationPreference((boolean) newValue);
539                             return true;
540                         });
541             }
542 
543             if (mAlertHistory != null) {
544                 mAlertHistory.setOnPreferenceClickListener(
545                         preference -> {
546                             final Intent intent = new Intent(getContext(),
547                                     CellBroadcastListActivity.class);
548                             startActivity(intent);
549                             return true;
550                         });
551             }
552 
553             updateVibrationPreference(sp.getBoolean(CellBroadcastSettings.KEY_OVERRIDE_DND,
554                     false));
555             updatePreferenceVisibility();
556         }
557 
558         /**
559          * Update the vibration preference based on override DND. If DND is overridden, then do
560          * not allow users to turn off vibration.
561          *
562          * @param overrideDnd {@code true} if the alert will be played at full volume, regardless
563          * DND settings.
564          */
updateVibrationPreference(boolean overrideDnd)565         private void updateVibrationPreference(boolean overrideDnd) {
566             if (mEnableVibrateCheckBox != null) {
567                 if (overrideDnd) {
568                     // If DND is enabled, always enable vibration.
569                     mEnableVibrateCheckBox.setChecked(true);
570                 }
571                 // Grey out the preference if DND is overridden.
572                 mEnableVibrateCheckBox.setEnabled(!overrideDnd);
573             }
574         }
575 
576         /**
577          * Dynamically update each preference's visibility based on configuration.
578          */
updatePreferenceVisibility()579         private void updatePreferenceVisibility() {
580             Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext());
581 
582             CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager(
583                     getContext(), SubscriptionManager.getDefaultSubscriptionId());
584 
585             if (mMasterToggle != null) {
586                 mMasterToggle.setVisible(res.getBoolean(R.bool.show_main_switch_settings));
587             }
588 
589             if (mPresidentialCheckBox != null) {
590                 mPresidentialCheckBox.setVisible(
591                         res.getBoolean(R.bool.show_presidential_alerts_settings));
592             }
593 
594             if (mExtremeCheckBox != null) {
595                 mExtremeCheckBox.setVisible(res.getBoolean(R.bool.show_extreme_alert_settings)
596                         && !channelManager.getCellBroadcastChannelRanges(
597                                 R.array.cmas_alert_extreme_channels_range_strings).isEmpty());
598             }
599 
600             if (mSevereCheckBox != null) {
601                 mSevereCheckBox.setVisible(res.getBoolean(R.bool.show_severe_alert_settings)
602                         && !channelManager.getCellBroadcastChannelRanges(
603                                 R.array.cmas_alerts_severe_range_strings).isEmpty());
604             }
605 
606             if (mAmberCheckBox != null) {
607                 mAmberCheckBox.setVisible(res.getBoolean(R.bool.show_amber_alert_settings)
608                         && !channelManager.getCellBroadcastChannelRanges(
609                                 R.array.cmas_amber_alerts_channels_range_strings).isEmpty());
610             }
611 
612             if (mPublicSafetyMessagesChannelCheckBox != null) {
613                 mPublicSafetyMessagesChannelCheckBox.setVisible(
614                         res.getBoolean(R.bool.show_public_safety_settings)
615                                 && !channelManager.getCellBroadcastChannelRanges(
616                                         R.array.public_safety_messages_channels_range_strings)
617                                 .isEmpty());
618             }
619             // this is the matching full screen settings for public safety toggle. shown only if
620             // public safety toggle is displayed.
621             if (mPublicSafetyMessagesChannelFullScreenCheckBox != null) {
622                 mPublicSafetyMessagesChannelFullScreenCheckBox.setVisible(
623                         res.getBoolean(R.bool.show_public_safety_full_screen_settings)
624                                 && (mPublicSafetyMessagesChannelCheckBox != null
625                                 && mPublicSafetyMessagesChannelCheckBox.isVisible()));
626             }
627 
628             if (mTestCheckBox != null) {
629                 mTestCheckBox.setVisible(isTestAlertsToggleVisible(getContext()));
630             }
631 
632             if (mExerciseTestCheckBox != null) {
633                 boolean visible = false;
634                 if (res.getBoolean(R.bool.show_separate_exercise_settings)) {
635                     if (res.getBoolean(R.bool.show_exercise_settings)
636                             || CellBroadcastReceiver.isTestingMode(getContext())) {
637                         if (!channelManager.getCellBroadcastChannelRanges(
638                                 R.array.exercise_alert_range_strings).isEmpty()) {
639                             visible = true;
640                         }
641                     }
642                 }
643                 mExerciseTestCheckBox.setVisible(visible);
644             }
645 
646             if (mOperatorDefinedCheckBox != null) {
647                 boolean visible = false;
648                 if (res.getBoolean(R.bool.show_separate_operator_defined_settings)) {
649                     if (res.getBoolean(R.bool.show_operator_defined_settings)
650                             || CellBroadcastReceiver.isTestingMode(getContext())) {
651                         if (!channelManager.getCellBroadcastChannelRanges(
652                                 R.array.operator_defined_alert_range_strings).isEmpty()) {
653                             visible = true;
654                         }
655                     }
656                 }
657                 mOperatorDefinedCheckBox.setVisible(visible);
658             }
659 
660             if (mEmergencyAlertsCheckBox != null) {
661                 mEmergencyAlertsCheckBox.setVisible(!channelManager.getCellBroadcastChannelRanges(
662                         R.array.emergency_alerts_channels_range_strings).isEmpty());
663             }
664 
665             if (mStateLocalTestCheckBox != null) {
666                 mStateLocalTestCheckBox.setVisible(
667                         res.getBoolean(R.bool.show_state_local_test_settings)
668                                 && !channelManager.getCellBroadcastChannelRanges(
669                                         R.array.state_local_test_alert_range_strings).isEmpty());
670             }
671 
672             if (mReceiveCmasInSecondLanguageCheckBox != null) {
673                 mReceiveCmasInSecondLanguageCheckBox.setVisible(!res.getString(
674                         R.string.emergency_alert_second_language_code).isEmpty());
675             }
676 
677             if (mAreaUpdateInfoCheckBox != null) {
678                 mAreaUpdateInfoCheckBox.setVisible(
679                         res.getBoolean(R.bool.config_showAreaUpdateInfoSettings));
680             }
681 
682             if (mOverrideDndCheckBox != null) {
683                 mOverrideDndCheckBox.setVisible(res.getBoolean(R.bool.show_override_dnd_settings));
684             }
685 
686             if (mEnableVibrateCheckBox != null) {
687                 // Only show vibrate toggle when override DND toggle is available to users, or when
688                 // override DND default is turned off.
689                 // In some countries, override DND is always on, which means vibration is always on.
690                 // In that case, no need to show vibration toggle for users.
691                 Vibrator vibrator = getContext().getSystemService(Vibrator.class);
692                 boolean supportVibration = (vibrator != null) && vibrator.hasVibrator();
693                 mEnableVibrateCheckBox.setVisible(supportVibration
694                         && (res.getBoolean(R.bool.show_override_dnd_settings) ||
695                         !res.getBoolean(R.bool.override_dnd)));
696             }
697             if (mAlertsHeader != null) {
698                 mAlertsHeader.setVisible(
699                         !getContext().getString(R.string.alerts_header_summary).isEmpty());
700             }
701 
702             if (mSpeechCheckBox != null) {
703                 mSpeechCheckBox.setVisible(res.getBoolean(R.bool.show_alert_speech_setting)
704                         || getActivity().getPackageManager()
705                         .hasSystemFeature(PackageManager.FEATURE_WATCH));
706             }
707         }
708 
initReminderIntervalList()709         private void initReminderIntervalList() {
710             Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext());
711 
712             String[] activeValues =
713                     res.getStringArray(R.array.alert_reminder_interval_active_values);
714             String[] allEntries = res.getStringArray(R.array.alert_reminder_interval_entries);
715             String[] newEntries = new String[activeValues.length];
716 
717             // Only add active interval to the list
718             for (int i = 0; i < activeValues.length; i++) {
719                 int index = mReminderInterval.findIndexOfValue(activeValues[i]);
720                 if (index != -1) {
721                     newEntries[i] = allEntries[index];
722                     if (DBG) Log.d(TAG, "Added " + allEntries[index]);
723                 } else {
724                     Log.e(TAG, "Can't find " + activeValues[i]);
725                 }
726             }
727 
728             mReminderInterval.setEntries(newEntries);
729             mReminderInterval.setEntryValues(activeValues);
730             mReminderInterval.setSummary(mReminderInterval.getEntry());
731             mReminderInterval.setOnPreferenceChangeListener(
732                     new Preference.OnPreferenceChangeListener() {
733                         @Override
734                         public boolean onPreferenceChange(Preference pref, Object newValue) {
735                             final ListPreference listPref = (ListPreference) pref;
736                             final int idx = listPref.findIndexOfValue((String) newValue);
737                             listPref.setSummary(listPref.getEntries()[idx]);
738                             return true;
739                         }
740                     });
741         }
742 
743 
setAlertsEnabled(boolean alertsEnabled)744         private void setAlertsEnabled(boolean alertsEnabled) {
745             if (mSevereCheckBox != null) {
746                 mSevereCheckBox.setEnabled(alertsEnabled);
747                 mSevereCheckBox.setChecked(alertsEnabled);
748             }
749             if (mExtremeCheckBox != null) {
750                 mExtremeCheckBox.setEnabled(alertsEnabled);
751                 mExtremeCheckBox.setChecked(alertsEnabled);
752             }
753             if (mAmberCheckBox != null) {
754                 mAmberCheckBox.setEnabled(alertsEnabled);
755                 mAmberCheckBox.setChecked(alertsEnabled);
756             }
757             if (mAreaUpdateInfoCheckBox != null) {
758                 mAreaUpdateInfoCheckBox.setEnabled(alertsEnabled);
759                 mAreaUpdateInfoCheckBox.setChecked(alertsEnabled);
760                 notifyAreaInfoUpdate(alertsEnabled);
761             }
762             if (mEmergencyAlertsCheckBox != null) {
763                 mEmergencyAlertsCheckBox.setEnabled(alertsEnabled);
764                 mEmergencyAlertsCheckBox.setChecked(alertsEnabled);
765             }
766             if (mPublicSafetyMessagesChannelCheckBox != null) {
767                 mPublicSafetyMessagesChannelCheckBox.setEnabled(alertsEnabled);
768                 mPublicSafetyMessagesChannelCheckBox.setChecked(alertsEnabled);
769             }
770             if (mStateLocalTestCheckBox != null) {
771                 mStateLocalTestCheckBox.setEnabled(alertsEnabled);
772                 mStateLocalTestCheckBox.setChecked(alertsEnabled);
773             }
774             if (mTestCheckBox != null) {
775                 mTestCheckBox.setEnabled(alertsEnabled);
776                 mTestCheckBox.setChecked(alertsEnabled);
777             }
778             if (mExerciseTestCheckBox != null) {
779                 mExerciseTestCheckBox.setEnabled(alertsEnabled);
780                 mExerciseTestCheckBox.setChecked(alertsEnabled);
781             }
782             if (mOperatorDefinedCheckBox != null) {
783                 mOperatorDefinedCheckBox.setEnabled(alertsEnabled);
784                 mOperatorDefinedCheckBox.setChecked(alertsEnabled);
785             }
786         }
787 
notifyAreaInfoUpdate(boolean enabled)788         private void notifyAreaInfoUpdate(boolean enabled) {
789             Intent areaInfoIntent = new Intent(AREA_INFO_UPDATE_ACTION);
790             areaInfoIntent.putExtra(AREA_INFO_UPDATE_ENABLED_EXTRA, enabled);
791             // sending broadcast protected by the permission which is only
792             // granted for CBR mainline module.
793             getContext().sendBroadcast(areaInfoIntent, CBR_MODULE_PERMISSION);
794         }
795 
796 
797         @Override
onResume()798         public void onResume() {
799             super.onResume();
800             updatePreferenceVisibility();
801         }
802 
803         @Override
onDestroy()804         public void onDestroy() {
805             super.onDestroy();
806             LocalBroadcastManager.getInstance(getContext())
807                     .unregisterReceiver(mTestingModeChangedReceiver);
808         }
809     }
810 
isTestAlertsToggleVisible(Context context)811     public static boolean isTestAlertsToggleVisible(Context context) {
812         CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager(context,
813                 SubscriptionManager.getDefaultSubscriptionId());
814         Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(context);
815         boolean isTestAlertsAvailable = !channelManager.getCellBroadcastChannelRanges(
816                 R.array.required_monthly_test_range_strings).isEmpty()
817                 || (!channelManager.getCellBroadcastChannelRanges(
818                 R.array.exercise_alert_range_strings).isEmpty()
819                 /** exercise toggle is controlled under the main test toggle */
820                 && (!res.getBoolean(R.bool.show_separate_exercise_settings)))
821                 || (!channelManager.getCellBroadcastChannelRanges(
822                 R.array.operator_defined_alert_range_strings).isEmpty()
823                 /** operator defined toggle is controlled under the main test toggle */
824                 && (!res.getBoolean(R.bool.show_separate_operator_defined_settings)))
825                 || !channelManager.getCellBroadcastChannelRanges(
826                 R.array.etws_test_alerts_range_strings).isEmpty();
827 
828         return (res.getBoolean(R.bool.show_test_settings)
829                 || CellBroadcastReceiver.isTestingMode(context))
830                 && isTestAlertsAvailable;
831     }
832 
isFeatureEnabled(Context context, String feature, boolean defaultValue)833     public static boolean isFeatureEnabled(Context context, String feature, boolean defaultValue) {
834         CarrierConfigManager configManager =
835                 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
836 
837         if (configManager != null) {
838             PersistableBundle carrierConfig = configManager.getConfig();
839             if (carrierConfig != null) {
840                 return carrierConfig.getBoolean(feature, defaultValue);
841             }
842         }
843 
844         return defaultValue;
845     }
846 
847     /**
848      * Override used by tests so that we don't call
849      * SubscriptionManager.getResourcesForSubId, which is a static unmockable
850      * method.
851      */
852     @VisibleForTesting
setUseResourcesForSubId(boolean useResourcesForSubId)853     public static void setUseResourcesForSubId(boolean useResourcesForSubId) {
854         sUseResourcesForSubId = useResourcesForSubId;
855     }
856 
857     /**
858      * Get the device resource based on SIM
859      *
860      * @param context Context
861      * @param subId Subscription index
862      *
863      * @return The resource
864      */
getResources(@onNull Context context, int subId)865     public static @NonNull Resources getResources(@NonNull Context context, int subId) {
866         // based on the latest design, subId can be valid earlier than mcc mnc is known to telephony
867         // check if sim is loaded to avoid caching the wrong resources.
868         TelephonyManager tm = context.getSystemService(TelephonyManager.class);
869         boolean isSimLoaded = tm.getSimApplicationState(SubscriptionManager.getSlotIndex(subId))
870                 == TelephonyManager.SIM_STATE_LOADED;
871         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID
872                 || !SubscriptionManager.isValidSubscriptionId(subId) || !sUseResourcesForSubId
873                 || !isSimLoaded) {
874             return context.getResources();
875         }
876 
877         if (sResourcesCache.containsKey(subId)) {
878             return sResourcesCache.get(subId);
879         }
880 
881         Resources res = SubscriptionManager.getResourcesForSubId(context, subId);
882         sResourcesCache.put(subId, res);
883 
884         return res;
885     }
886 
887     /**
888      * Get the resources using the default subscription ID.
889      * @param context Context
890      * @return the Resources for the default subscription ID, or if there is no default subscription
891      * from SubscriptionManager, the resources for the latest loaded SIM.
892      */
getResourcesForDefaultSubId(@onNull Context context)893     public static @NonNull Resources getResourcesForDefaultSubId(@NonNull Context context) {
894         return getResources(context, SubscriptionManager.getDefaultSubscriptionId());
895     }
896 }
897