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