Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05a2f1c6 authored by Daniel Norman's avatar Daniel Norman
Browse files

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
parent 65c26755
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -24,6 +24,16 @@ flag {
    bug: "271490102"
}

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"
+34 −0
Original line number Diff line number Diff line
@@ -2098,14 +2098,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
    /**
     * 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        }
    }

    /**
     * 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(),
+58 −0
Original line number Diff line number Diff line
@@ -1707,6 +1707,64 @@ public class AccessibilityManagerServiceTest {
                .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(