1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.accessibility; 18 19 import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; 20 import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled; 21 import static com.android.settings.accessibility.PreferredShortcuts.retrieveUserShortcutType; 22 23 import android.accessibilityservice.AccessibilityServiceInfo; 24 import android.app.Activity; 25 import android.app.Dialog; 26 import android.app.admin.DevicePolicyManager; 27 import android.app.settings.SettingsEnums; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.DialogInterface; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.pm.ApplicationInfo; 36 import android.content.pm.ResolveInfo; 37 import android.content.pm.ServiceInfo; 38 import android.net.Uri; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.UserHandle; 42 import android.os.storage.StorageManager; 43 import android.provider.Settings; 44 import android.text.TextUtils; 45 import android.util.Log; 46 import android.view.Menu; 47 import android.view.MenuInflater; 48 import android.view.View; 49 import android.view.accessibility.AccessibilityManager; 50 import android.widget.Switch; 51 52 import androidx.annotation.Nullable; 53 54 import com.android.internal.widget.LockPatternUtils; 55 import com.android.settings.R; 56 import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; 57 import com.android.settings.overlay.FeatureFactory; 58 import com.android.settings.password.ConfirmDeviceCredentialActivity; 59 import com.android.settingslib.accessibility.AccessibilityUtils; 60 61 import java.util.List; 62 import java.util.concurrent.atomic.AtomicBoolean; 63 64 /** Fragment for providing toggle bar and basic accessibility service setup. */ 65 public class ToggleAccessibilityServicePreferenceFragment extends 66 ToggleFeaturePreferenceFragment { 67 68 private static final String TAG = "ToggleAccessibilityServicePreferenceFragment"; 69 private static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1; 70 private LockPatternUtils mLockPatternUtils; 71 private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false); 72 73 private static final String EMPTY_STRING = ""; 74 75 private final SettingsContentObserver mSettingsContentObserver = 76 new SettingsContentObserver(new Handler()) { 77 @Override 78 public void onChange(boolean selfChange, Uri uri) { 79 updateSwitchBarToggleSwitch(); 80 } 81 }; 82 83 private Dialog mDialog; 84 private BroadcastReceiver mPackageRemovedReceiver; 85 86 @Override getMetricsCategory()87 public int getMetricsCategory() { 88 // Retrieve from getArguments() directly because this function will be executed from 89 // onAttach(), but variable mComponentName only available after onProcessArguments() 90 // which comes from onCreateView(). 91 final ComponentName componentName = getArguments().getParcelable( 92 AccessibilitySettings.EXTRA_COMPONENT_NAME); 93 94 return FeatureFactory.getFactory(getActivity().getApplicationContext()) 95 .getAccessibilityMetricsFeatureProvider() 96 .getDownloadedFeatureMetricsCategory(componentName); 97 } 98 99 @Override onCreateOptionsMenu(Menu menu, MenuInflater infalter)100 public void onCreateOptionsMenu(Menu menu, MenuInflater infalter) { 101 // Do not call super. We don't want to see the "Help & feedback" option on this page so as 102 // not to confuse users who think they might be able to send feedback about a specific 103 // accessibility service from this page. 104 } 105 106 @Override onCreate(Bundle savedInstanceState)107 public void onCreate(Bundle savedInstanceState) { 108 super.onCreate(savedInstanceState); 109 mLockPatternUtils = new LockPatternUtils(getPrefContext()); 110 } 111 112 @Override onStart()113 public void onStart() { 114 super.onStart(); 115 final AccessibilityServiceInfo serviceInfo = getAccessibilityServiceInfo(); 116 if (serviceInfo == null) { 117 getActivity().finishAndRemoveTask(); 118 } else if (!AccessibilityUtil.isSystemApp(serviceInfo)) { 119 registerPackageRemoveReceiver(); 120 } 121 } 122 123 @Override onResume()124 public void onResume() { 125 super.onResume(); 126 updateSwitchBarToggleSwitch(); 127 mSettingsContentObserver.register(getContentResolver()); 128 } 129 130 @Override onPreferenceToggled(String preferenceKey, boolean enabled)131 public void onPreferenceToggled(String preferenceKey, boolean enabled) { 132 ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey); 133 logAccessibilityServiceEnabled(toggledService, enabled); 134 AccessibilityUtils.setAccessibilityServiceState(getPrefContext(), toggledService, enabled); 135 } 136 137 // IMPORTANT: Refresh the info since there are dynamically changing 138 // capabilities. For 139 // example, before JellyBean MR2 the user was granting the explore by touch 140 // one. 141 @Nullable getAccessibilityServiceInfo()142 AccessibilityServiceInfo getAccessibilityServiceInfo() { 143 final List<AccessibilityServiceInfo> infos = AccessibilityManager.getInstance( 144 getPrefContext()).getInstalledAccessibilityServiceList(); 145 146 for (int i = 0, count = infos.size(); i < count; i++) { 147 AccessibilityServiceInfo serviceInfo = infos.get(i); 148 ResolveInfo resolveInfo = serviceInfo.getResolveInfo(); 149 if (mComponentName.getPackageName().equals(resolveInfo.serviceInfo.packageName) 150 && mComponentName.getClassName().equals(resolveInfo.serviceInfo.name)) { 151 return serviceInfo; 152 } 153 } 154 return null; 155 } 156 157 @Override onCreateDialog(int dialogId)158 public Dialog onCreateDialog(int dialogId) { 159 switch (dialogId) { 160 case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: { 161 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 162 if (info == null) { 163 return null; 164 } 165 mDialog = AccessibilityServiceWarning 166 .createCapabilitiesDialog(getPrefContext(), info, 167 this::onDialogButtonFromEnableToggleClicked, 168 this::onDialogButtonFromUninstallClicked); 169 break; 170 } 171 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: { 172 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 173 if (info == null) { 174 return null; 175 } 176 mDialog = AccessibilityServiceWarning 177 .createCapabilitiesDialog(getPrefContext(), info, 178 this::onDialogButtonFromShortcutToggleClicked, 179 this::onDialogButtonFromUninstallClicked); 180 break; 181 } 182 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: { 183 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 184 if (info == null) { 185 return null; 186 } 187 mDialog = AccessibilityServiceWarning 188 .createCapabilitiesDialog(getPrefContext(), info, 189 this::onDialogButtonFromShortcutClicked, 190 this::onDialogButtonFromUninstallClicked); 191 break; 192 } 193 case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: { 194 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 195 if (info == null) { 196 return null; 197 } 198 mDialog = AccessibilityServiceWarning 199 .createDisableDialog(getPrefContext(), info, 200 this::onDialogButtonFromDisableToggleClicked); 201 break; 202 } 203 default: { 204 mDialog = super.onCreateDialog(dialogId); 205 } 206 } 207 return mDialog; 208 } 209 210 @Override getDialogMetricsCategory(int dialogId)211 public int getDialogMetricsCategory(int dialogId) { 212 switch (dialogId) { 213 case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: 214 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: 215 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: 216 return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_ENABLE; 217 case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: 218 return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_DISABLE; 219 case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: 220 return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL; 221 default: 222 return super.getDialogMetricsCategory(dialogId); 223 } 224 } 225 226 @Override getUserShortcutTypes()227 int getUserShortcutTypes() { 228 return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), 229 mComponentName); 230 } 231 232 @Override updateSwitchBarToggleSwitch()233 protected void updateSwitchBarToggleSwitch() { 234 final boolean checked = isAccessibilityServiceEnabled(); 235 if (mToggleServiceSwitchPreference.isChecked() == checked) { 236 return; 237 } 238 mToggleServiceSwitchPreference.setChecked(checked); 239 } 240 isAccessibilityServiceEnabled()241 private boolean isAccessibilityServiceEnabled() { 242 return AccessibilityUtils.getEnabledServicesFromSettings(getPrefContext()) 243 .contains(mComponentName); 244 } 245 246 /** 247 * Return whether the device is encrypted with legacy full disk encryption. Newer devices 248 * should be using File Based Encryption. 249 * 250 * @return true if device is encrypted 251 */ isFullDiskEncrypted()252 private boolean isFullDiskEncrypted() { 253 return StorageManager.isNonDefaultBlockEncrypted(); 254 } 255 256 @Override onActivityResult(int requestCode, int resultCode, Intent data)257 public void onActivityResult(int requestCode, int resultCode, Intent data) { 258 if (requestCode == ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION) { 259 if (resultCode == Activity.RESULT_OK) { 260 handleConfirmServiceEnabled(/* confirmed= */ true); 261 // The user confirmed that they accept weaker encryption when 262 // enabling the accessibility service, so change encryption. 263 // Since we came here asynchronously, check encryption again. 264 if (isFullDiskEncrypted()) { 265 mLockPatternUtils.clearEncryptionPassword(); 266 Settings.Global.putInt(getContentResolver(), 267 Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, 0); 268 } 269 } else { 270 handleConfirmServiceEnabled(/* confirmed= */ false); 271 } 272 } 273 } 274 registerPackageRemoveReceiver()275 private void registerPackageRemoveReceiver() { 276 if (mPackageRemovedReceiver != null || getContext() == null) { 277 return; 278 } 279 mPackageRemovedReceiver = new BroadcastReceiver() { 280 @Override 281 public void onReceive(Context context, Intent intent) { 282 final String packageName = intent.getData().getSchemeSpecificPart(); 283 if (TextUtils.equals(mComponentName.getPackageName(), packageName)) { 284 getActivity().finishAndRemoveTask(); 285 } 286 } 287 }; 288 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 289 filter.addDataScheme("package"); 290 getContext().registerReceiver(mPackageRemovedReceiver, filter); 291 } 292 unregisterPackageRemoveReceiver()293 private void unregisterPackageRemoveReceiver() { 294 if (mPackageRemovedReceiver == null || getContext() == null) { 295 return; 296 } 297 getContext().unregisterReceiver(mPackageRemovedReceiver); 298 mPackageRemovedReceiver = null; 299 } 300 isServiceSupportAccessibilityButton()301 private boolean isServiceSupportAccessibilityButton() { 302 final AccessibilityManager ams = getPrefContext().getSystemService( 303 AccessibilityManager.class); 304 final List<AccessibilityServiceInfo> services = ams.getInstalledAccessibilityServiceList(); 305 306 for (AccessibilityServiceInfo info : services) { 307 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { 308 ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo; 309 if (serviceInfo != null && TextUtils.equals(serviceInfo.name, 310 getAccessibilityServiceInfo().getResolveInfo().serviceInfo.name)) { 311 return true; 312 } 313 } 314 } 315 316 return false; 317 } 318 handleConfirmServiceEnabled(boolean confirmed)319 private void handleConfirmServiceEnabled(boolean confirmed) { 320 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed); 321 onPreferenceToggled(mPreferenceKey, confirmed); 322 } 323 createConfirmCredentialReasonMessage()324 private String createConfirmCredentialReasonMessage() { 325 int resId = R.string.enable_service_password_reason; 326 switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId())) { 327 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: { 328 resId = R.string.enable_service_pattern_reason; 329 } 330 break; 331 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 332 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: { 333 resId = R.string.enable_service_pin_reason; 334 } 335 break; 336 } 337 return getString(resId, getAccessibilityServiceInfo().getResolveInfo() 338 .loadLabel(getPackageManager())); 339 } 340 341 @Override onSwitchChanged(Switch switchView, boolean isChecked)342 public void onSwitchChanged(Switch switchView, boolean isChecked) { 343 if (isChecked != isAccessibilityServiceEnabled()) { 344 onPreferenceClick(isChecked); 345 } 346 } 347 348 @Override onToggleClicked(ShortcutPreference preference)349 public void onToggleClicked(ShortcutPreference preference) { 350 final int shortcutTypes = retrieveUserShortcutType(getPrefContext(), 351 mComponentName.flattenToString(), UserShortcutType.SOFTWARE); 352 if (preference.isChecked()) { 353 if (!mToggleServiceSwitchPreference.isChecked()) { 354 preference.setChecked(false); 355 showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE); 356 } else { 357 AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, 358 mComponentName); 359 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 360 } 361 } else { 362 AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, 363 mComponentName); 364 } 365 mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); 366 } 367 368 @Override onSettingsClicked(ShortcutPreference preference)369 public void onSettingsClicked(ShortcutPreference preference) { 370 final boolean isServiceOnOrShortcutAdded = mShortcutPreference.isChecked() 371 || mToggleServiceSwitchPreference.isChecked(); 372 showPopupDialog(isServiceOnOrShortcutAdded ? DialogEnums.EDIT_SHORTCUT 373 : DialogEnums.ENABLE_WARNING_FROM_SHORTCUT); 374 } 375 376 @Override onProcessArguments(Bundle arguments)377 protected void onProcessArguments(Bundle arguments) { 378 super.onProcessArguments(arguments); 379 // Settings title and intent. 380 String settingsTitle = arguments.getString(AccessibilitySettings.EXTRA_SETTINGS_TITLE); 381 String settingsComponentName = arguments.getString( 382 AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME); 383 if (!TextUtils.isEmpty(settingsTitle) && !TextUtils.isEmpty(settingsComponentName)) { 384 Intent settingsIntent = new Intent(Intent.ACTION_MAIN).setComponent( 385 ComponentName.unflattenFromString(settingsComponentName.toString())); 386 if (!getPackageManager().queryIntentActivities(settingsIntent, 0).isEmpty()) { 387 mSettingsTitle = settingsTitle; 388 mSettingsIntent = settingsIntent; 389 setHasOptionsMenu(true); 390 } 391 } 392 393 mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME); 394 395 // Settings animated image. 396 final int animatedImageRes = arguments.getInt( 397 AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES); 398 if (animatedImageRes > 0) { 399 mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 400 .authority(mComponentName.getPackageName()) 401 .appendPath(String.valueOf(animatedImageRes)) 402 .build(); 403 } 404 405 // Get Accessibility service name. 406 mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel( 407 getPackageManager()); 408 } 409 onDialogButtonFromDisableToggleClicked(DialogInterface dialog, int which)410 private void onDialogButtonFromDisableToggleClicked(DialogInterface dialog, int which) { 411 switch (which) { 412 case DialogInterface.BUTTON_POSITIVE: 413 handleConfirmServiceEnabled(/* confirmed= */ false); 414 break; 415 case DialogInterface.BUTTON_NEGATIVE: 416 handleConfirmServiceEnabled(/* confirmed= */ true); 417 break; 418 default: 419 throw new IllegalArgumentException("Unexpected button identifier"); 420 } 421 } 422 onDialogButtonFromEnableToggleClicked(View view)423 private void onDialogButtonFromEnableToggleClicked(View view) { 424 final int viewId = view.getId(); 425 if (viewId == R.id.permission_enable_allow_button) { 426 onAllowButtonFromEnableToggleClicked(); 427 } else if (viewId == R.id.permission_enable_deny_button) { 428 onDenyButtonFromEnableToggleClicked(); 429 } else { 430 throw new IllegalArgumentException("Unexpected view id"); 431 } 432 } 433 onDialogButtonFromUninstallClicked()434 private void onDialogButtonFromUninstallClicked() { 435 mDialog.dismiss(); 436 final Intent uninstallIntent = createUninstallPackageActivityIntent(); 437 if (uninstallIntent == null) { 438 return; 439 } 440 startActivity(uninstallIntent); 441 } 442 443 @Nullable createUninstallPackageActivityIntent()444 private Intent createUninstallPackageActivityIntent() { 445 final AccessibilityServiceInfo a11yServiceInfo = getAccessibilityServiceInfo(); 446 if (a11yServiceInfo == null) { 447 Log.w(TAG, "createUnInstallIntent -- invalid a11yServiceInfo"); 448 return null; 449 } 450 final ApplicationInfo appInfo = 451 a11yServiceInfo.getResolveInfo().serviceInfo.applicationInfo; 452 final Uri packageUri = Uri.parse("package:" + appInfo.packageName); 453 final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri); 454 return uninstallIntent; 455 } 456 457 @Override onStop()458 public void onStop() { 459 super.onStop(); 460 unregisterPackageRemoveReceiver(); 461 } 462 onAllowButtonFromEnableToggleClicked()463 private void onAllowButtonFromEnableToggleClicked() { 464 if (isFullDiskEncrypted()) { 465 final String title = createConfirmCredentialReasonMessage(); 466 final Intent intent = ConfirmDeviceCredentialActivity.createIntent(title, /* details= */ 467 null); 468 startActivityForResult(intent, 469 ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION); 470 } else { 471 handleConfirmServiceEnabled(/* confirmed= */ true); 472 if (isServiceSupportAccessibilityButton()) { 473 mIsDialogShown.set(false); 474 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 475 } 476 } 477 478 mDialog.dismiss(); 479 } 480 onDenyButtonFromEnableToggleClicked()481 private void onDenyButtonFromEnableToggleClicked() { 482 handleConfirmServiceEnabled(/* confirmed= */ false); 483 mDialog.dismiss(); 484 } 485 onDialogButtonFromShortcutToggleClicked(View view)486 void onDialogButtonFromShortcutToggleClicked(View view) { 487 final int viewId = view.getId(); 488 if (viewId == R.id.permission_enable_allow_button) { 489 onAllowButtonFromShortcutToggleClicked(); 490 } else if (viewId == R.id.permission_enable_deny_button) { 491 onDenyButtonFromShortcutToggleClicked(); 492 } else { 493 throw new IllegalArgumentException("Unexpected view id"); 494 } 495 } 496 onAllowButtonFromShortcutToggleClicked()497 private void onAllowButtonFromShortcutToggleClicked() { 498 mShortcutPreference.setChecked(true); 499 500 final int shortcutTypes = retrieveUserShortcutType(getPrefContext(), 501 mComponentName.flattenToString(), UserShortcutType.SOFTWARE); 502 AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); 503 504 mIsDialogShown.set(false); 505 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 506 507 mDialog.dismiss(); 508 509 mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); 510 } 511 onDenyButtonFromShortcutToggleClicked()512 private void onDenyButtonFromShortcutToggleClicked() { 513 mShortcutPreference.setChecked(false); 514 515 mDialog.dismiss(); 516 } 517 onDialogButtonFromShortcutClicked(View view)518 void onDialogButtonFromShortcutClicked(View view) { 519 final int viewId = view.getId(); 520 if (viewId == R.id.permission_enable_allow_button) { 521 onAllowButtonFromShortcutClicked(); 522 } else if (viewId == R.id.permission_enable_deny_button) { 523 onDenyButtonFromShortcutClicked(); 524 } else { 525 throw new IllegalArgumentException("Unexpected view id"); 526 } 527 } 528 onAllowButtonFromShortcutClicked()529 private void onAllowButtonFromShortcutClicked() { 530 mIsDialogShown.set(false); 531 showPopupDialog(DialogEnums.EDIT_SHORTCUT); 532 533 mDialog.dismiss(); 534 } 535 onDenyButtonFromShortcutClicked()536 private void onDenyButtonFromShortcutClicked() { 537 mDialog.dismiss(); 538 } 539 onPreferenceClick(boolean isChecked)540 private boolean onPreferenceClick(boolean isChecked) { 541 if (isChecked) { 542 mToggleServiceSwitchPreference.setChecked(false); 543 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, 544 /* disableService */ false); 545 if (!mShortcutPreference.isChecked()) { 546 showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_TOGGLE); 547 } else { 548 handleConfirmServiceEnabled(/* confirmed= */ true); 549 if (isServiceSupportAccessibilityButton()) { 550 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 551 } 552 } 553 } else { 554 mToggleServiceSwitchPreference.setChecked(true); 555 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, 556 /* enableService */ true); 557 showDialog(DialogEnums.DISABLE_WARNING_FROM_TOGGLE); 558 } 559 return true; 560 } 561 showPopupDialog(int dialogId)562 private void showPopupDialog(int dialogId) { 563 if (mIsDialogShown.compareAndSet(/* expect= */ false, /* update= */ true)) { 564 showDialog(dialogId); 565 setOnDismissListener( 566 dialog -> mIsDialogShown.compareAndSet(/* expect= */ true, /* update= */ 567 false)); 568 } 569 } 570 } 571