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