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.app.ActionBar; 20 import android.app.Activity; 21 import android.app.Fragment; 22 import android.app.backup.BackupManager; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.content.res.Resources; 27 import android.os.Bundle; 28 import android.os.PersistableBundle; 29 import android.os.UserManager; 30 import android.provider.Settings; 31 import android.telephony.CarrierConfigManager; 32 import android.telephony.SubscriptionManager; 33 import android.util.Log; 34 import android.view.MenuItem; 35 36 import androidx.preference.ListPreference; 37 import androidx.preference.Preference; 38 import androidx.preference.PreferenceCategory; 39 import androidx.preference.PreferenceFragment; 40 import androidx.preference.PreferenceScreen; 41 import androidx.preference.TwoStatePreference; 42 43 /** 44 * Settings activity for the cell broadcast receiver. 45 */ 46 public class CellBroadcastSettings extends Activity { 47 48 private static final String TAG = "CellBroadcastSettings"; 49 50 private static final boolean DBG = false; 51 52 // Preference key for a master toggle to enable/disable all alerts message (default enabled). 53 public static final String KEY_ENABLE_ALERTS_MASTER_TOGGLE = "enable_alerts_master_toggle"; 54 55 // Preference key for whether to enable public safety messages (default enabled). 56 public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES = "enable_public_safety_messages"; 57 58 // Preference key for whether to enable emergency alerts (default enabled). 59 public static final String KEY_ENABLE_EMERGENCY_ALERTS = "enable_emergency_alerts"; 60 61 // Enable vibration on alert (unless master volume is silent). 62 public static final String KEY_ENABLE_ALERT_VIBRATE = "enable_alert_vibrate"; 63 64 // Speak contents of alert after playing the alert sound. 65 public static final String KEY_ENABLE_ALERT_SPEECH = "enable_alert_speech"; 66 67 // Always play at full volume when playing the alert sound. 68 public static final String KEY_USE_FULL_VOLUME = "use_full_volume"; 69 70 // Preference category for emergency alert and CMAS settings. 71 public static final String KEY_CATEGORY_EMERGENCY_ALERTS = "category_emergency_alerts"; 72 73 // Preference category for alert preferences. 74 public static final String KEY_CATEGORY_ALERT_PREFERENCES = "category_alert_preferences"; 75 76 // Whether to display CMAS extreme threat notifications (default is enabled). 77 public static final String KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS = 78 "enable_cmas_extreme_threat_alerts"; 79 80 // Whether to display CMAS severe threat notifications (default is enabled). 81 public static final String KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS = 82 "enable_cmas_severe_threat_alerts"; 83 84 // Whether to display CMAS amber alert messages (default is enabled). 85 public static final String KEY_ENABLE_CMAS_AMBER_ALERTS = "enable_cmas_amber_alerts"; 86 87 // Preference category for development settings (enabled by settings developer options toggle). 88 public static final String KEY_CATEGORY_DEV_SETTINGS = "category_dev_settings"; 89 90 // Whether to display monthly test messages (default is disabled). 91 public static final String KEY_ENABLE_TEST_ALERTS = "enable_test_alerts"; 92 93 // Whether to display state/local test messages (default disabled). 94 public static final String KEY_ENABLE_STATE_LOCAL_TEST_ALERTS = 95 "enable_state_local_test_alerts"; 96 97 // Preference key for whether to enable area update information notifications 98 // Enabled by default for phones sold in Brazil and India, otherwise this setting may be hidden. 99 public static final String KEY_ENABLE_AREA_UPDATE_INFO_ALERTS = 100 "enable_area_update_info_alerts"; 101 102 // Preference key for initial opt-in/opt-out dialog. 103 public static final String KEY_SHOW_CMAS_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; 104 105 // Alert reminder interval ("once" = single 2 minute reminder). 106 public static final String KEY_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; 107 108 // Preference key for emergency alerts history 109 public static final String KEY_EMERGENCY_ALERT_HISTORY = "emergency_alert_history"; 110 111 // For watch layout 112 private static final String KEY_WATCH_ALERT_REMINDER = "watch_alert_reminder"; 113 114 @Override onCreate(Bundle savedInstanceState)115 public void onCreate(Bundle savedInstanceState) { 116 super.onCreate(savedInstanceState); 117 118 ActionBar actionBar = getActionBar(); 119 if (actionBar != null) { 120 // android.R.id.home will be triggered in onOptionsItemSelected() 121 actionBar.setDisplayHomeAsUpEnabled(true); 122 } 123 124 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 125 if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS)) { 126 setContentView(R.layout.cell_broadcast_disallowed_preference_screen); 127 return; 128 } 129 130 // We only add new CellBroadcastSettingsFragment if no fragment is restored. 131 Fragment fragment = getFragmentManager().findFragmentById(android.R.id.content); 132 if (fragment == null) { 133 getFragmentManager().beginTransaction().add(android.R.id.content, 134 new CellBroadcastSettingsFragment()).commit(); 135 } 136 } 137 138 @Override onOptionsItemSelected(MenuItem item)139 public boolean onOptionsItemSelected(MenuItem item) { 140 switch (item.getItemId()) { 141 // Respond to the action bar's Up/Home button 142 case android.R.id.home: 143 finish(); 144 return true; 145 } 146 return super.onOptionsItemSelected(item); 147 } 148 149 /** 150 * New fragment-style implementation of preferences. 151 */ 152 public static class CellBroadcastSettingsFragment extends PreferenceFragment { 153 154 private TwoStatePreference mExtremeCheckBox; 155 private TwoStatePreference mSevereCheckBox; 156 private TwoStatePreference mAmberCheckBox; 157 private TwoStatePreference mMasterToggle; 158 private TwoStatePreference mPublicSafetyMessagesChannelCheckBox; 159 private TwoStatePreference mEmergencyAlertsCheckBox; 160 private ListPreference mReminderInterval; 161 private TwoStatePreference mSpeechCheckBox; 162 private TwoStatePreference mFullVolumeCheckBox; 163 private TwoStatePreference mAreaUpdateInfoCheckBox; 164 private TwoStatePreference mTestCheckBox; 165 private TwoStatePreference mStateLocalTestCheckBox; 166 private Preference mAlertHistory; 167 private PreferenceCategory mAlertCategory; 168 private PreferenceCategory mAlertPreferencesCategory; 169 private PreferenceCategory mDevSettingCategory; 170 private boolean mDisableSevereWhenExtremeDisabled = true; 171 172 // WATCH 173 private TwoStatePreference mAlertReminder; 174 175 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)176 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 177 178 // Load the preferences from an XML resource 179 PackageManager pm = getActivity().getPackageManager(); 180 if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 181 addPreferencesFromResource(R.xml.watch_preferences); 182 } else { 183 addPreferencesFromResource(R.xml.preferences); 184 } 185 186 PreferenceScreen preferenceScreen = getPreferenceScreen(); 187 188 mExtremeCheckBox = (TwoStatePreference) 189 findPreference(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS); 190 mSevereCheckBox = (TwoStatePreference) 191 findPreference(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS); 192 mAmberCheckBox = (TwoStatePreference) 193 findPreference(KEY_ENABLE_CMAS_AMBER_ALERTS); 194 mMasterToggle = (TwoStatePreference) 195 findPreference(KEY_ENABLE_ALERTS_MASTER_TOGGLE); 196 mPublicSafetyMessagesChannelCheckBox = (TwoStatePreference) 197 findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES); 198 mEmergencyAlertsCheckBox = (TwoStatePreference) 199 findPreference(KEY_ENABLE_EMERGENCY_ALERTS); 200 mReminderInterval = (ListPreference) 201 findPreference(KEY_ALERT_REMINDER_INTERVAL); 202 mSpeechCheckBox = (TwoStatePreference) 203 findPreference(KEY_ENABLE_ALERT_SPEECH); 204 mFullVolumeCheckBox = (TwoStatePreference) 205 findPreference(KEY_USE_FULL_VOLUME); 206 mAreaUpdateInfoCheckBox = (TwoStatePreference) 207 findPreference(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS); 208 mTestCheckBox = (TwoStatePreference) 209 findPreference(KEY_ENABLE_TEST_ALERTS); 210 mStateLocalTestCheckBox = (TwoStatePreference) 211 findPreference(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS); 212 mAlertHistory = findPreference(KEY_EMERGENCY_ALERT_HISTORY); 213 mDevSettingCategory = (PreferenceCategory) 214 findPreference(KEY_CATEGORY_DEV_SETTINGS); 215 216 if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 217 mAlertReminder = (TwoStatePreference) 218 findPreference(KEY_WATCH_ALERT_REMINDER); 219 if (Integer.valueOf(mReminderInterval.getValue()) == 0) { 220 mAlertReminder.setChecked(false); 221 } else { 222 mAlertReminder.setChecked(true); 223 } 224 mAlertReminder.setOnPreferenceChangeListener((p, newVal) -> { 225 try { 226 mReminderInterval.setValueIndex((Boolean) newVal ? 1 : 3); 227 } catch (IndexOutOfBoundsException e) { 228 mReminderInterval.setValue(String.valueOf(0)); 229 Log.w(TAG, "Setting default value"); 230 } 231 return true; 232 }); 233 PreferenceScreen watchScreen = (PreferenceScreen) 234 findPreference(KEY_CATEGORY_ALERT_PREFERENCES); 235 watchScreen.removePreference(mReminderInterval); 236 } else { 237 mAlertPreferencesCategory = (PreferenceCategory) 238 findPreference(KEY_CATEGORY_ALERT_PREFERENCES); 239 mAlertCategory = (PreferenceCategory) 240 findPreference(KEY_CATEGORY_EMERGENCY_ALERTS); 241 } 242 243 244 mDisableSevereWhenExtremeDisabled = isFeatureEnabled(getContext(), 245 CarrierConfigManager.KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true); 246 247 // Handler for settings that require us to reconfigure enabled channels in radio 248 Preference.OnPreferenceChangeListener startConfigServiceListener = 249 new Preference.OnPreferenceChangeListener() { 250 @Override 251 public boolean onPreferenceChange(Preference pref, Object newValue) { 252 CellBroadcastReceiver.startConfigService(pref.getContext()); 253 254 if (mDisableSevereWhenExtremeDisabled) { 255 if (pref.getKey().equals(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS)) { 256 boolean isExtremeAlertChecked = (Boolean) newValue; 257 if (mSevereCheckBox != null) { 258 mSevereCheckBox.setEnabled(isExtremeAlertChecked); 259 mSevereCheckBox.setChecked(false); 260 } 261 } 262 } 263 264 if (pref.getKey().equals(KEY_ENABLE_ALERTS_MASTER_TOGGLE)) { 265 boolean isEnableAlerts = (Boolean) newValue; 266 setAlertsEnabled(isEnableAlerts); 267 } 268 269 // Notify backup manager a backup pass is needed. 270 new BackupManager(getContext()).dataChanged(); 271 return true; 272 } 273 }; 274 275 // Show extra settings when developer options is enabled in settings. 276 boolean enableDevSettings = Settings.Global.getInt(getContext().getContentResolver(), 277 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; 278 279 Resources res = getResourcesForDefaultSmsSubscriptionId(getContext()); 280 initReminderIntervalList(); 281 282 boolean emergencyAlertOnOffOptionEnabled = isFeatureEnabled(getContext(), 283 CarrierConfigManager.KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false); 284 285 if (enableDevSettings || emergencyAlertOnOffOptionEnabled) { 286 // enable/disable all alerts except CMAS presidential alerts. 287 if (mMasterToggle != null) { 288 mMasterToggle.setOnPreferenceChangeListener(startConfigServiceListener); 289 // If allow alerts are disabled, we turn all sub-alerts off. If it's enabled, we 290 // leave them as they are. 291 if (!mMasterToggle.isChecked()) { 292 setAlertsEnabled(false); 293 } 294 } 295 } else { 296 if (mMasterToggle != null) preferenceScreen.removePreference(mMasterToggle); 297 } 298 299 boolean hideTestAlertMenu = CellBroadcastSettings.isFeatureEnabled(getContext(), 300 CarrierConfigManager.KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false); 301 302 // Check if we want to hide the test alert toggle. 303 if (hideTestAlertMenu || !enableDevSettings || !isTestAlertsAvailable()) { 304 if (mTestCheckBox != null) { 305 mAlertCategory.removePreference(mTestCheckBox); 306 } 307 } 308 309 if (!enableDevSettings && !pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 310 if (mDevSettingCategory != null) { 311 preferenceScreen.removePreference(mDevSettingCategory); 312 } 313 } 314 315 // Remove preferences 316 if (!res.getBoolean(R.bool.show_cmas_settings)) { 317 // Remove CMAS preference items in emergency alert category. 318 if (mAlertCategory != null) { 319 if (mExtremeCheckBox != null) mAlertCategory.removePreference(mExtremeCheckBox); 320 if (mSevereCheckBox != null) mAlertCategory.removePreference(mSevereCheckBox); 321 if (mAmberCheckBox != null) mAlertCategory.removePreference(mAmberCheckBox); 322 } 323 } 324 325 if (!Resources.getSystem().getBoolean( 326 com.android.internal.R.bool.config_showAreaUpdateInfoSettings)) { 327 if (mAlertCategory != null) { 328 if (mAreaUpdateInfoCheckBox != null) { 329 mAlertCategory.removePreference(mAreaUpdateInfoCheckBox); 330 } 331 } 332 } 333 334 // Remove preferences based on range configurations 335 if (CellBroadcastChannelManager.getCellBroadcastChannelRanges( 336 this.getContext(), 337 R.array.public_safety_messages_channels_range_strings).isEmpty()) { 338 // Remove public safety messages 339 if (mAlertCategory != null) { 340 if (mPublicSafetyMessagesChannelCheckBox != null) { 341 mAlertCategory.removePreference(mPublicSafetyMessagesChannelCheckBox); 342 } 343 } 344 } 345 346 if (CellBroadcastChannelManager.getCellBroadcastChannelRanges( 347 this.getContext(), R.array.emergency_alerts_channels_range_strings).isEmpty()) { 348 // Remove emergency alert messages 349 if (mAlertCategory != null) { 350 if (mEmergencyAlertsCheckBox != null) { 351 mAlertCategory.removePreference(mEmergencyAlertsCheckBox); 352 } 353 } 354 } 355 356 if (CellBroadcastChannelManager.getCellBroadcastChannelRanges( 357 this.getContext(), 358 R.array.state_local_test_alert_range_strings).isEmpty()) { 359 // Remove state local test messages 360 if (mAlertCategory != null) { 361 if (mStateLocalTestCheckBox != null) { 362 mAlertCategory.removePreference(mStateLocalTestCheckBox); 363 } 364 } 365 } 366 367 if (mAreaUpdateInfoCheckBox != null) { 368 mAreaUpdateInfoCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 369 } 370 if (mExtremeCheckBox != null) { 371 mExtremeCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 372 } 373 if (mPublicSafetyMessagesChannelCheckBox != null) { 374 mPublicSafetyMessagesChannelCheckBox.setOnPreferenceChangeListener( 375 startConfigServiceListener); 376 } 377 if (mEmergencyAlertsCheckBox != null) { 378 mEmergencyAlertsCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 379 } 380 if (mSevereCheckBox != null) { 381 mSevereCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 382 if (mDisableSevereWhenExtremeDisabled) { 383 if (mExtremeCheckBox != null) { 384 mSevereCheckBox.setEnabled(mExtremeCheckBox.isChecked()); 385 } 386 } 387 } 388 if (mAmberCheckBox != null) { 389 mAmberCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 390 } 391 if (mTestCheckBox != null) { 392 mTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 393 } 394 if (mStateLocalTestCheckBox != null) { 395 mStateLocalTestCheckBox.setOnPreferenceChangeListener( 396 startConfigServiceListener); 397 } 398 399 if (mAlertHistory != null) { 400 mAlertHistory.setOnPreferenceClickListener( 401 new Preference.OnPreferenceClickListener() { 402 @Override 403 public boolean onPreferenceClick(final Preference preference) { 404 final Intent intent = new Intent(getContext(), 405 CellBroadcastListActivity.class); 406 startActivity(intent); 407 return true; 408 } 409 }); 410 } 411 } 412 isTestAlertsAvailable()413 private boolean isTestAlertsAvailable() { 414 return !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 415 this.getContext(), R.array.required_monthly_test_range_strings).isEmpty() 416 || !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 417 this.getContext(), R.array.exercise_alert_range_strings).isEmpty() 418 || !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 419 this.getContext(), R.array.operator_defined_alert_range_strings) 420 .isEmpty() 421 || !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 422 this.getContext(), R.array.etws_test_alerts_range_strings).isEmpty(); 423 } 424 initReminderIntervalList()425 private void initReminderIntervalList() { 426 Resources res = getResourcesForDefaultSmsSubscriptionId(getContext()); 427 428 String[] activeValues = 429 res.getStringArray(R.array.alert_reminder_interval_active_values); 430 String[] allEntries = res.getStringArray(R.array.alert_reminder_interval_entries); 431 String[] newEntries = new String[activeValues.length]; 432 433 // Only add active interval to the list 434 for (int i = 0; i < activeValues.length; i++) { 435 int index = mReminderInterval.findIndexOfValue(activeValues[i]); 436 if (index != -1) { 437 newEntries[i] = allEntries[index]; 438 if (DBG) Log.d(TAG, "Added " + allEntries[index]); 439 } else { 440 Log.e(TAG, "Can't find " + activeValues[i]); 441 } 442 } 443 444 mReminderInterval.setEntries(newEntries); 445 mReminderInterval.setEntryValues(activeValues); 446 mReminderInterval.setSummary(mReminderInterval.getEntry()); 447 mReminderInterval.setOnPreferenceChangeListener( 448 new Preference.OnPreferenceChangeListener() { 449 @Override 450 public boolean onPreferenceChange(Preference pref, Object newValue) { 451 final ListPreference listPref = (ListPreference) pref; 452 final int idx = listPref.findIndexOfValue((String) newValue); 453 listPref.setSummary(listPref.getEntries()[idx]); 454 return true; 455 } 456 }); 457 } 458 459 setAlertsEnabled(boolean alertsEnabled)460 private void setAlertsEnabled(boolean alertsEnabled) { 461 if (mSevereCheckBox != null) { 462 mSevereCheckBox.setEnabled(alertsEnabled); 463 mSevereCheckBox.setChecked(alertsEnabled); 464 } 465 if (mExtremeCheckBox != null) { 466 mExtremeCheckBox.setEnabled(alertsEnabled); 467 mExtremeCheckBox.setChecked(alertsEnabled); 468 } 469 if (mAmberCheckBox != null) { 470 mAmberCheckBox.setEnabled(alertsEnabled); 471 mAmberCheckBox.setChecked(alertsEnabled); 472 } 473 if (mAreaUpdateInfoCheckBox != null) { 474 mAreaUpdateInfoCheckBox.setEnabled(alertsEnabled); 475 mAreaUpdateInfoCheckBox.setChecked(alertsEnabled); 476 } 477 if (mAlertPreferencesCategory != null) { 478 mAlertPreferencesCategory.setEnabled(alertsEnabled); 479 } 480 if (mDevSettingCategory != null) { 481 mDevSettingCategory.setEnabled(alertsEnabled); 482 } 483 if (mEmergencyAlertsCheckBox != null) { 484 mEmergencyAlertsCheckBox.setEnabled(alertsEnabled); 485 mEmergencyAlertsCheckBox.setChecked(alertsEnabled); 486 } 487 if (mPublicSafetyMessagesChannelCheckBox != null) { 488 mPublicSafetyMessagesChannelCheckBox.setEnabled(alertsEnabled); 489 mPublicSafetyMessagesChannelCheckBox.setChecked(alertsEnabled); 490 } 491 if (mStateLocalTestCheckBox != null) { 492 mStateLocalTestCheckBox.setEnabled(alertsEnabled); 493 mStateLocalTestCheckBox.setChecked(alertsEnabled); 494 } 495 } 496 } 497 isFeatureEnabled(Context context, String feature, boolean defaultValue)498 public static boolean isFeatureEnabled(Context context, String feature, boolean defaultValue) { 499 int subId = SubscriptionManager.getDefaultSmsSubscriptionId(); 500 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 501 subId = SubscriptionManager.getDefaultSubscriptionId(); 502 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 503 return defaultValue; 504 } 505 } 506 507 CarrierConfigManager configManager = 508 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 509 510 if (configManager != null) { 511 PersistableBundle carrierConfig = configManager.getConfigForSubId(subId); 512 513 if (carrierConfig != null) { 514 return carrierConfig.getBoolean(feature, defaultValue); 515 } 516 } 517 518 return defaultValue; 519 } 520 getResourcesForDefaultSmsSubscriptionId(Context context)521 public static Resources getResourcesForDefaultSmsSubscriptionId(Context context) { 522 int subId = SubscriptionManager.getDefaultSmsSubscriptionId(); 523 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 524 subId = SubscriptionManager.getDefaultSubscriptionId(); 525 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 526 return context.getResources(); 527 } 528 } 529 return SubscriptionManager.getResourcesForSubId(context, subId); 530 } 531 } 532