Clears the default service from A11Y_SHORTCUT B&R.
If the restored value contains the default service then it is removed,
unless the previous value (pre-restore) also contains the default
service.
Bug: 341374402
Flag: com.android.server.accessibility.clear_default_from_a11y_shortcut_target_service_restore
Test: atest AccessibilityManagerServiceTest
Test: Create a backup with the default service in the Setting.
- Restore backup when the Setting is empty.
Observe Setting is unchanged.
- Restore backup when the Setting has another service.
Observe Setting is unchanged.
- Restore backup when the Setting already has the default service.
Observe Setting keeps the default service.
Change-Id: Id7f9e6de96f032abf1f073cbef801fd2dbcee9a1
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index a50fb9a..1c57dd3 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -25,6 +25,16 @@
}
flag {
+ name: "clear_default_from_a11y_shortcut_target_service_restore"
+ namespace: "accessibility"
+ description: "Clears the config_defaultAccessibilityService from B&R for ACCESSIBILITY_SHORTCUT_TARGET_SERVICE."
+ bug: "341374402"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "compute_window_changes_on_a11y_v2"
namespace: "accessibility"
description: "Computes accessibility window changes in accessibility instead of wm package."
@@ -209,4 +219,4 @@
namespace: "accessibility"
description: "Feature allows users to change color correction saturation for daltonizer."
bug: "322829049"
-}
\ No newline at end of file
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c55e9ea..f3dd635 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2098,14 +2098,36 @@
/**
* Merges the old and restored value of
* {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
+ *
+ * <p>Also clears out {@link R.string#config_defaultAccessibilityService} from
+ * the merged set if it was not present before restoring.
*/
private void restoreAccessibilityShortcutTargetService(
String oldValue, String restoredValue) {
final Set<String> targetsFromSetting = new ArraySet<>();
readColonDelimitedStringToSet(oldValue, str -> str,
targetsFromSetting, /*doMerge=*/false);
+ final String defaultService =
+ mContext.getString(R.string.config_defaultAccessibilityService);
+ final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
+ ? null : ComponentName.unflattenFromString(defaultService);
+ boolean shouldClearDefaultService = defaultServiceComponent != null
+ && !stringSetContainsComponentName(targetsFromSetting, defaultServiceComponent);
readColonDelimitedStringToSet(restoredValue, str -> str,
targetsFromSetting, /*doMerge=*/true);
+ if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()) {
+ if (shouldClearDefaultService && stringSetContainsComponentName(
+ targetsFromSetting, defaultServiceComponent)) {
+ Slog.i(LOG_TAG, "Removing default service " + defaultService
+ + " from restore of "
+ + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+ targetsFromSetting.removeIf(str ->
+ defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
+ }
+ if (targetsFromSetting.isEmpty()) {
+ return;
+ }
+ }
synchronized (mLock) {
final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
final Set<String> shortcutTargets =
@@ -2120,6 +2142,18 @@
}
}
+ /**
+ * Returns {@code true} if the set contains the provided non-null {@link ComponentName}.
+ *
+ * <p>This ignores values in the set that are not valid {@link ComponentName}s.
+ */
+ private boolean stringSetContainsComponentName(Set<String> set,
+ @NonNull ComponentName componentName) {
+ return componentName != null && set.stream()
+ .map(ComponentName::unflattenFromString)
+ .anyMatch(componentName::equals);
+ }
+
private int getClientStateLocked(AccessibilityUserState userState) {
return userState.getClientStateLocked(
mUiAutomationManager.canIntrospect(),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 78cd2c1..fe5144e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -1707,6 +1707,64 @@
.containsExactlyElementsIn(expected);
}
+ @Test
+ @EnableFlags({
+ android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
+ Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+ public void restoreA11yShortcutTargetService_alreadyHadDefaultService_doesNotClear() {
+ final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultAccessibilityService, serviceDefault);
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ setupShortcutTargetServices(userState);
+
+ broadcastSettingRestored(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ /*previousValue=*/serviceDefault,
+ /*newValue=*/serviceDefault);
+
+ final Set<String> expected = Set.of(serviceDefault);
+ assertThat(readStringsFromSetting(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ .containsExactlyElementsIn(expected);
+ assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
+ .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ .containsExactlyElementsIn(expected);
+ }
+
+ @Test
+ @EnableFlags({
+ android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
+ Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+ public void restoreA11yShortcutTargetService_didNotHaveDefaultService_clearsDefaultService() {
+ final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+ final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
+ // Restored value from the broadcast contains both default and non-default service.
+ final String combinedRestored = String.join(":", serviceDefault, serviceRestored);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultAccessibilityService, serviceDefault);
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ setupShortcutTargetServices(userState);
+
+ broadcastSettingRestored(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ /*previousValue=*/null,
+ /*newValue=*/combinedRestored);
+
+ // The default service is cleared from the final restored value.
+ final Set<String> expected = Set.of(serviceRestored);
+ assertThat(readStringsFromSetting(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ .containsExactlyElementsIn(expected);
+ assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
+ .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ .containsExactlyElementsIn(expected);
+ }
+
private Set<String> readStringsFromSetting(String setting) {
final Set<String> result = new ArraySet<>();
mA11yms.readColonDelimitedSettingToSet(