Loading core/java/com/android/internal/accessibility/util/ShortcutUtils.java +21 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,27 @@ public final class ShortcutUtils { } } /** * Converts {@link Settings.Secure} key to {@link UserShortcutType}. * * @param key The shortcut key in Settings. * @return The mapped type */ @UserShortcutType public static int convertToType(String key) { return switch (key) { case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE; case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS; case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE; case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED -> UserShortcutType.TRIPLETAP; case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED -> UserShortcutType.TWOFINGER_DOUBLETAP; default -> throw new IllegalArgumentException( "Unsupported user shortcut key: " + key); }; } /** * Updates an accessibility state if the accessibility service is a Always-On a11y service, * a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON Loading services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +125 −188 File changed.Preview size limit exceeded, changes collapsed. Show changes services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +33 −13 Original line number Diff line number Diff line Loading @@ -777,12 +777,15 @@ class AccessibilityUserState { * @return The array set of the strings */ public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) { return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType)); } private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) { if (shortcutType == UserShortcutType.HARDWARE) { return mAccessibilityShortcutKeyTargets; } else if (shortcutType == UserShortcutType.SOFTWARE) { return mAccessibilityButtonTargets; } else if (shortcutType == UserShortcutType.QUICK_SETTINGS) { return getA11yQsTargets(); return mAccessibilityQsTargets; } else if ((shortcutType == UserShortcutType.TRIPLETAP && isMagnificationSingleFingerTripleTapEnabledLocked()) || ( shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP Loading @@ -794,6 +797,32 @@ class AccessibilityUserState { return new ArraySet<>(); } /** * Updates the corresponding shortcut targets with the provided set. * Tap shortcuts don't operate using sets of targets, * so trying to update {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} * will instead throw an {@code IllegalArgumentException} * @param newTargets set of targets to replace the existing set. * @param shortcutType type to be replaced. * @return {@code true} if the set was changed, or {@code false} if the elements are the same. * @throws IllegalArgumentException if {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} is used. */ boolean updateShortcutTargetsLocked( Set<String> newTargets, @UserShortcutType int shortcutType) { final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP; if ((shortcutType & mask) != 0) { throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets."); } final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType); if (newTargets.equals(currentTargets)) { return false; } currentTargets.clear(); currentTargets.addAll(newTargets); return true; } /** * Whether or not the given shortcut target is installed in device. * Loading Loading @@ -844,8 +873,9 @@ class AccessibilityUserState { ); } Set<String> targets = getShortcutTargetsLocked(shortcutType); boolean result = targets.removeIf(name -> { // getting internal set lets us directly modify targets, as it's not a copy. Set<String> targets = getShortcutTargetsInternalLocked(shortcutType); return targets.removeIf(name -> { ComponentName componentName; if (name == null || (componentName = ComponentName.unflattenFromString(name)) == null) { Loading @@ -853,11 +883,6 @@ class AccessibilityUserState { } return componentName.equals(target); }); if (shortcutType == UserShortcutType.QUICK_SETTINGS) { updateA11yQsTargetLocked(targets); } return result; } /** Loading Loading @@ -1114,11 +1139,6 @@ class AccessibilityUserState { ); } public void updateA11yQsTargetLocked(Set<String> targets) { mAccessibilityQsTargets.clear(); mAccessibilityQsTargets.addAll(targets); } /** * Returns a copy of the targets which has qs shortcut turned on */ Loading services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +87 −45 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static android.view.accessibility.Flags.FLAG_SKIP_ACCESSIBILITY_WARNING_D import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; Loading Loading @@ -1082,7 +1084,7 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, UserShortcutType.HARDWARE, HARDWARE, List.of(target), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); Loading Loading @@ -1346,14 +1348,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, UserShortcutType.HARDWARE, HARDWARE, List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE, mTestableContext, HARDWARE, TARGET_STANDARD_A11Y_SERVICE.flattenToString()) ).isTrue(); } Loading @@ -1367,7 +1369,7 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ false, UserShortcutType.HARDWARE, HARDWARE, List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); Loading @@ -1375,7 +1377,7 @@ public class AccessibilityManagerServiceTest { assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE, HARDWARE, TARGET_STANDARD_A11Y_SERVICE.flattenToString())) .isFalse(); } Loading @@ -1390,14 +1392,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, UserShortcutType.QUICK_SETTINGS, QUICK_SETTINGS, List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, UserShortcutType.QUICK_SETTINGS, mTestableContext, QUICK_SETTINGS, TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()) ).isTrue(); verify(mStatusBarManagerInternal) Loading @@ -1417,14 +1419,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ false, UserShortcutType.QUICK_SETTINGS, QUICK_SETTINGS, List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, UserShortcutType.QUICK_SETTINGS, mTestableContext, QUICK_SETTINGS, TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()) ).isFalse(); verify(mStatusBarManagerInternal) Loading Loading @@ -1614,44 +1616,49 @@ public class AccessibilityManagerServiceTest { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) public void restoreAccessibilityQsTargets_a11yQsTargetsRestored() { public void restoreShortcutTargets_qs_a11yQsTargetsRestored() { String daltonizerTile = AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(); String colorInversionTile = AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); userState.updateA11yQsTargetLocked(Set.of(daltonizerTile)); userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_QS_TARGETS, /*previousValue=*/null, ShortcutUtils.convertToKey(QUICK_SETTINGS), /*newValue=*/colorInversionTile); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM).getA11yQsTargets()) .containsExactlyElementsIn(Set.of(daltonizerTile, colorInversionTile)); Set<String> expected = Set.of(daltonizerTile, colorInversionTile); assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS))) .containsExactlyElementsIn(expected); assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS)) .containsExactlyElementsIn(expected); } @Test @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) public void restoreAccessibilityQsTargets_a11yQsTargetsNotRestored() { public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() { String daltonizerTile = AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(); String colorInversionTile = AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); userState.updateA11yQsTargetLocked(Set.of(daltonizerTile)); userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS); putShortcutSettingForUser(QUICK_SETTINGS, daltonizerTile, userState.mUserId); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_QS_TARGETS, /*previousValue=*/null, ShortcutUtils.convertToKey(QUICK_SETTINGS), /*newValue=*/colorInversionTile); assertThat(userState.getA11yQsTargets()) .containsExactlyElementsIn(Set.of(daltonizerTile)); Set<String> expected = Set.of(daltonizerTile); assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS))) .containsExactlyElementsIn(expected); assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS)) .containsExactlyElementsIn(expected); } @Test Loading Loading @@ -1717,27 +1724,26 @@ public class AccessibilityManagerServiceTest { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreA11yShortcutTargetService_targetsMerged() { public void restoreShortcutTargets_hardware_targetsMerged() { mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); final String otherPrevious = TARGET_MAGNIFICATION; final String combinedPrevious = String.join(":", servicePrevious, otherPrevious); final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); mA11yms.enableShortcutsForTargets( true, HARDWARE, List.of(servicePrevious, otherPrevious), userState.mUserId); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /*previousValue=*/combinedPrevious, ShortcutUtils.convertToKey(HARDWARE), /*newValue=*/serviceRestored); final Set<String> expected = Set.of(servicePrevious, otherPrevious, serviceRestored); assertThat(readStringsFromSetting( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)) assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } Loading @@ -1745,7 +1751,7 @@ public class AccessibilityManagerServiceTest { @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() { public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); mTestableContext.getOrCreateTestableResources().addOverride( R.string.config_defaultAccessibilityService, serviceDefault); Loading @@ -1754,17 +1760,18 @@ public class AccessibilityManagerServiceTest { mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); // default is present in userState & setting, so it's not cleared putShortcutSettingForUser(HARDWARE, serviceDefault, UserHandle.USER_SYSTEM); userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE); 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)) assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } Loading @@ -1772,7 +1779,7 @@ public class AccessibilityManagerServiceTest { @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() { public void restoreShortcutTargets_hardware_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. Loading @@ -1784,18 +1791,45 @@ public class AccessibilityManagerServiceTest { mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /*previousValue=*/null, broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE), /*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)) assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) assertThat(userState.getShortcutTargetsLocked(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 restoreShortcutTargets_hardware_nullSetting_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); // UserState has default, but setting is null (this emulates a typical scenario in SUW). userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE); putShortcutSettingForUser(HARDWARE, null, UserHandle.USER_SYSTEM); broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE), /*newValue=*/combinedRestored); // The default service is cleared from the final restored value. final Set<String> expected = Set.of(serviceRestored); assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } Loading @@ -1806,11 +1840,10 @@ public class AccessibilityManagerServiceTest { return result; } private void broadcastSettingRestored(String setting, String previousValue, String newValue) { private void broadcastSettingRestored(String setting, String newValue) { Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) .putExtra(Intent.EXTRA_SETTING_NAME, setting) .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue) .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, newValue); sendBroadcastToAccessibilityManagerService(intent); mTestableLooper.processAllMessages(); Loading Loading @@ -1952,4 +1985,13 @@ public class AccessibilityManagerServiceTest { private static boolean isSameCurrentUser(AccessibilityManagerService service, Context context) { return service.getCurrentUserIdLocked() == context.getUserId(); } private void putShortcutSettingForUser(@UserShortcutType int shortcutType, String shortcutValue, int userId) { Settings.Secure.putStringForUser( mTestableContext.getContentResolver(), ShortcutUtils.convertToKey(shortcutType), shortcutValue, userId); } } services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +4 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSI import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; import static com.google.common.truth.Truth.assertThat; Loading Loading @@ -429,20 +430,20 @@ public class AccessibilityUserStateTest { } @Test public void updateA11yQsTargetLocked_valueUpdated() { public void updateShortcutTargetsLocked_quickSettings_valueUpdated() { Set<String> newTargets = Set.of( AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(), AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString() ); mUserState.updateA11yQsTargetLocked(newTargets); mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS); assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets); } @Test public void getA11yQsTargets_returnsCopiedData() { updateA11yQsTargetLocked_valueUpdated(); updateShortcutTargetsLocked_quickSettings_valueUpdated(); Set<String> targets = mUserState.getA11yQsTargets(); targets.clear(); Loading Loading
core/java/com/android/internal/accessibility/util/ShortcutUtils.java +21 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,27 @@ public final class ShortcutUtils { } } /** * Converts {@link Settings.Secure} key to {@link UserShortcutType}. * * @param key The shortcut key in Settings. * @return The mapped type */ @UserShortcutType public static int convertToType(String key) { return switch (key) { case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE; case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS; case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE; case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED -> UserShortcutType.TRIPLETAP; case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED -> UserShortcutType.TWOFINGER_DOUBLETAP; default -> throw new IllegalArgumentException( "Unsupported user shortcut key: " + key); }; } /** * Updates an accessibility state if the accessibility service is a Always-On a11y service, * a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +125 −188 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +33 −13 Original line number Diff line number Diff line Loading @@ -777,12 +777,15 @@ class AccessibilityUserState { * @return The array set of the strings */ public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) { return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType)); } private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) { if (shortcutType == UserShortcutType.HARDWARE) { return mAccessibilityShortcutKeyTargets; } else if (shortcutType == UserShortcutType.SOFTWARE) { return mAccessibilityButtonTargets; } else if (shortcutType == UserShortcutType.QUICK_SETTINGS) { return getA11yQsTargets(); return mAccessibilityQsTargets; } else if ((shortcutType == UserShortcutType.TRIPLETAP && isMagnificationSingleFingerTripleTapEnabledLocked()) || ( shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP Loading @@ -794,6 +797,32 @@ class AccessibilityUserState { return new ArraySet<>(); } /** * Updates the corresponding shortcut targets with the provided set. * Tap shortcuts don't operate using sets of targets, * so trying to update {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} * will instead throw an {@code IllegalArgumentException} * @param newTargets set of targets to replace the existing set. * @param shortcutType type to be replaced. * @return {@code true} if the set was changed, or {@code false} if the elements are the same. * @throws IllegalArgumentException if {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} is used. */ boolean updateShortcutTargetsLocked( Set<String> newTargets, @UserShortcutType int shortcutType) { final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP; if ((shortcutType & mask) != 0) { throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets."); } final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType); if (newTargets.equals(currentTargets)) { return false; } currentTargets.clear(); currentTargets.addAll(newTargets); return true; } /** * Whether or not the given shortcut target is installed in device. * Loading Loading @@ -844,8 +873,9 @@ class AccessibilityUserState { ); } Set<String> targets = getShortcutTargetsLocked(shortcutType); boolean result = targets.removeIf(name -> { // getting internal set lets us directly modify targets, as it's not a copy. Set<String> targets = getShortcutTargetsInternalLocked(shortcutType); return targets.removeIf(name -> { ComponentName componentName; if (name == null || (componentName = ComponentName.unflattenFromString(name)) == null) { Loading @@ -853,11 +883,6 @@ class AccessibilityUserState { } return componentName.equals(target); }); if (shortcutType == UserShortcutType.QUICK_SETTINGS) { updateA11yQsTargetLocked(targets); } return result; } /** Loading Loading @@ -1114,11 +1139,6 @@ class AccessibilityUserState { ); } public void updateA11yQsTargetLocked(Set<String> targets) { mAccessibilityQsTargets.clear(); mAccessibilityQsTargets.addAll(targets); } /** * Returns a copy of the targets which has qs shortcut turned on */ Loading
services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +87 −45 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static android.view.accessibility.Flags.FLAG_SKIP_ACCESSIBILITY_WARNING_D import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; Loading Loading @@ -1082,7 +1084,7 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, UserShortcutType.HARDWARE, HARDWARE, List.of(target), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); Loading Loading @@ -1346,14 +1348,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, UserShortcutType.HARDWARE, HARDWARE, List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE, mTestableContext, HARDWARE, TARGET_STANDARD_A11Y_SERVICE.flattenToString()) ).isTrue(); } Loading @@ -1367,7 +1369,7 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ false, UserShortcutType.HARDWARE, HARDWARE, List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); Loading @@ -1375,7 +1377,7 @@ public class AccessibilityManagerServiceTest { assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE, HARDWARE, TARGET_STANDARD_A11Y_SERVICE.flattenToString())) .isFalse(); } Loading @@ -1390,14 +1392,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, UserShortcutType.QUICK_SETTINGS, QUICK_SETTINGS, List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, UserShortcutType.QUICK_SETTINGS, mTestableContext, QUICK_SETTINGS, TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()) ).isTrue(); verify(mStatusBarManagerInternal) Loading @@ -1417,14 +1419,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ false, UserShortcutType.QUICK_SETTINGS, QUICK_SETTINGS, List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, UserShortcutType.QUICK_SETTINGS, mTestableContext, QUICK_SETTINGS, TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()) ).isFalse(); verify(mStatusBarManagerInternal) Loading Loading @@ -1614,44 +1616,49 @@ public class AccessibilityManagerServiceTest { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) public void restoreAccessibilityQsTargets_a11yQsTargetsRestored() { public void restoreShortcutTargets_qs_a11yQsTargetsRestored() { String daltonizerTile = AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(); String colorInversionTile = AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); userState.updateA11yQsTargetLocked(Set.of(daltonizerTile)); userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_QS_TARGETS, /*previousValue=*/null, ShortcutUtils.convertToKey(QUICK_SETTINGS), /*newValue=*/colorInversionTile); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM).getA11yQsTargets()) .containsExactlyElementsIn(Set.of(daltonizerTile, colorInversionTile)); Set<String> expected = Set.of(daltonizerTile, colorInversionTile); assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS))) .containsExactlyElementsIn(expected); assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS)) .containsExactlyElementsIn(expected); } @Test @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) public void restoreAccessibilityQsTargets_a11yQsTargetsNotRestored() { public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() { String daltonizerTile = AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(); String colorInversionTile = AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); userState.updateA11yQsTargetLocked(Set.of(daltonizerTile)); userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS); putShortcutSettingForUser(QUICK_SETTINGS, daltonizerTile, userState.mUserId); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_QS_TARGETS, /*previousValue=*/null, ShortcutUtils.convertToKey(QUICK_SETTINGS), /*newValue=*/colorInversionTile); assertThat(userState.getA11yQsTargets()) .containsExactlyElementsIn(Set.of(daltonizerTile)); Set<String> expected = Set.of(daltonizerTile); assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS))) .containsExactlyElementsIn(expected); assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS)) .containsExactlyElementsIn(expected); } @Test Loading Loading @@ -1717,27 +1724,26 @@ public class AccessibilityManagerServiceTest { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) public void restoreA11yShortcutTargetService_targetsMerged() { public void restoreShortcutTargets_hardware_targetsMerged() { mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); final String otherPrevious = TARGET_MAGNIFICATION; final String combinedPrevious = String.join(":", servicePrevious, otherPrevious); final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); mA11yms.enableShortcutsForTargets( true, HARDWARE, List.of(servicePrevious, otherPrevious), userState.mUserId); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /*previousValue=*/combinedPrevious, ShortcutUtils.convertToKey(HARDWARE), /*newValue=*/serviceRestored); final Set<String> expected = Set.of(servicePrevious, otherPrevious, serviceRestored); assertThat(readStringsFromSetting( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)) assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } Loading @@ -1745,7 +1751,7 @@ public class AccessibilityManagerServiceTest { @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() { public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); mTestableContext.getOrCreateTestableResources().addOverride( R.string.config_defaultAccessibilityService, serviceDefault); Loading @@ -1754,17 +1760,18 @@ public class AccessibilityManagerServiceTest { mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); // default is present in userState & setting, so it's not cleared putShortcutSettingForUser(HARDWARE, serviceDefault, UserHandle.USER_SYSTEM); userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE); 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)) assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } Loading @@ -1772,7 +1779,7 @@ public class AccessibilityManagerServiceTest { @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() { public void restoreShortcutTargets_hardware_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. Loading @@ -1784,18 +1791,45 @@ public class AccessibilityManagerServiceTest { mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /*previousValue=*/null, broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE), /*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)) assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) assertThat(userState.getShortcutTargetsLocked(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 restoreShortcutTargets_hardware_nullSetting_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); // UserState has default, but setting is null (this emulates a typical scenario in SUW). userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE); putShortcutSettingForUser(HARDWARE, null, UserHandle.USER_SYSTEM); broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE), /*newValue=*/combinedRestored); // The default service is cleared from the final restored value. final Set<String> expected = Set.of(serviceRestored); assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } Loading @@ -1806,11 +1840,10 @@ public class AccessibilityManagerServiceTest { return result; } private void broadcastSettingRestored(String setting, String previousValue, String newValue) { private void broadcastSettingRestored(String setting, String newValue) { Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) .putExtra(Intent.EXTRA_SETTING_NAME, setting) .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue) .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, newValue); sendBroadcastToAccessibilityManagerService(intent); mTestableLooper.processAllMessages(); Loading Loading @@ -1952,4 +1985,13 @@ public class AccessibilityManagerServiceTest { private static boolean isSameCurrentUser(AccessibilityManagerService service, Context context) { return service.getCurrentUserIdLocked() == context.getUserId(); } private void putShortcutSettingForUser(@UserShortcutType int shortcutType, String shortcutValue, int userId) { Settings.Secure.putStringForUser( mTestableContext.getContentResolver(), ShortcutUtils.convertToKey(shortcutType), shortcutValue, userId); } }
services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +4 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSI import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; import static com.google.common.truth.Truth.assertThat; Loading Loading @@ -429,20 +430,20 @@ public class AccessibilityUserStateTest { } @Test public void updateA11yQsTargetLocked_valueUpdated() { public void updateShortcutTargetsLocked_quickSettings_valueUpdated() { Set<String> newTargets = Set.of( AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(), AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString() ); mUserState.updateA11yQsTargetLocked(newTargets); mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS); assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets); } @Test public void getA11yQsTargets_returnsCopiedData() { updateA11yQsTargetLocked_valueUpdated(); updateShortcutTargetsLocked_quickSettings_valueUpdated(); Set<String> targets = mUserState.getA11yQsTargets(); targets.clear(); Loading