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