Loading packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +2 −10 Original line number Diff line number Diff line Loading @@ -4692,24 +4692,16 @@ public class SettingsProvider extends ContentProvider { if (currentVersion == 185) { // Deprecate ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, and migrate it // to ACCESSIBILITY_BUTTON_TARGET_COMPONENT. // to ACCESSIBILITY_BUTTON_TARGETS. final SettingsState secureSettings = getSecureSettingsLocked(userId); final Setting magnifyNavbarEnabled = secureSettings.getSettingLocked( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); if ("1".equals(magnifyNavbarEnabled.getValue())) { secureSettings.insertSettingLocked( Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, Secure.ACCESSIBILITY_BUTTON_TARGETS, ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER, null /* tag */, false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME); } else { // Clear a11y button targets list setting. A11yManagerService will end up // adding all legacy enabled services that want the button to the list, so // there's no need to keep tracking them. secureSettings.insertSettingLocked( Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, null, null /* tag */, false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME); } secureSettings.deleteSettingLocked( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); Loading services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +1 −0 Original line number Diff line number Diff line Loading @@ -1081,6 +1081,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ pw.append(", eventTypes=" + AccessibilityEvent.eventTypeToString(mEventTypes)); pw.append(", notificationTimeout=" + mNotificationTimeout); pw.append(", requestA11yBtn=" + mRequestAccessibilityButton); pw.append("]"); } } Loading services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +72 −23 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.view.accessibility.AccessibilityManager.ShortcutType; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; import android.Manifest; import android.accessibilityservice.AccessibilityGestureEvent; Loading Loading @@ -876,6 +877,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub throw new SecurityException("Caller does not hold permission " + android.Manifest.permission.STATUS_BAR_SERVICE); } if (targetName == null) { synchronized (mLock) { final AccessibilityUserState userState = getCurrentUserStateLocked(); targetName = userState.getTargetAssignedToAccessibilityButton(); } } mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::performAccessibilityShortcutInternal, this, displayId, ACCESSIBILITY_BUTTON, targetName)); Loading Loading @@ -1828,7 +1835,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readMagnificationEnabledSettingsLocked(userState); somethingChanged |= readAutoclickEnabledSettingLocked(userState); somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState); somethingChanged |= readAccessibilityButtonSettingsLocked(userState); somethingChanged |= readAccessibilityButtonTargetsLocked(userState); somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState); somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState); return somethingChanged; } Loading Loading @@ -1948,9 +1956,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) { private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) { final Set<String> targetsFromSetting = new ArraySet<>(); readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, targetsFromSetting, str -> str); final Set<String> currentTargets = Loading @@ -1964,6 +1972,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) { final String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId); if (TextUtils.isEmpty(componentId)) { if (userState.getTargetAssignedToAccessibilityButton() == null) { return false; } userState.setTargetAssignedToAccessibilityButton(null); return true; } if (componentId.equals(userState.getTargetAssignedToAccessibilityButton())) { return false; } userState.setTargetAssignedToAccessibilityButton(componentId); return true; } private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) { final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser( mContext.getContentResolver(), Loading @@ -1984,7 +2009,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } /** * Check if the targets that will be enabled by the accessibility shortcut key is installed. * Check if the target that will be enabled by the accessibility shortcut key is installed. * If it isn't, remove it from the list and associated setting so a side loaded service can't * spoof the package name of the default service. */ Loading Loading @@ -2145,7 +2170,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * 1) Update accessibility button availability to accessibility services. * 2) Check if the targets that will be enabled by the accessibility button is installed. * 2) Check if the target that will be enabled by the accessibility button is installed. * If it isn't, remove it from the list and associated setting so a side loaded service can't * spoof the package name of the default service. */ Loading @@ -2172,8 +2197,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Update setting key with new value. persistColonDelimitedSetToSettingLocked( Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, currentTargets, str -> str); scheduleNotifyClientsOfServicesStateChangeLocked(userState); } Loading @@ -2182,7 +2206,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * 1) Check if the service assigned to accessibility button target sdk version > Q. * If it isn't, remove it from the list and associated setting. * (It happens when an accessibility service package is downgraded.) * 2) Check if an enabled service targeting sdk version > Q and requesting a11y button is * 2) For a service targeting sdk version > Q and requesting a11y button, it should be in the * enabled list if's assigned to a11y button. * (It happens when an accessibility service package is same graded, and updated requesting * a11y button flag) * 3) Check if an enabled service targeting sdk version > Q and requesting a11y button is * assigned to a shortcut. If it isn't, assigns it to the accessibility button. * (It happens when an enabled accessibility service package is upgraded.) * Loading @@ -2207,11 +2235,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo .targetSdkVersion > Build.VERSION_CODES.Q) { return false; } .targetSdkVersion <= Build.VERSION_CODES.Q) { // A11y services targeting sdk version <= Q should not be in the list. Slog.v(LOG_TAG, "Legacy service " + componentName + " should not in the button"); return true; } final boolean requestA11yButton = (serviceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; if (requestA11yButton && !userState.mEnabledServices.contains(componentName)) { // An a11y service targeting sdk version > Q and request A11y button and is assigned // to a11y btn should be in the enabled list. Slog.v(LOG_TAG, "Service requesting a11y button and be assigned to the button" + componentName + " should be enabled state"); return true; } return false; }); boolean changed = (lastSize != buttonTargets.size()); lastSize = buttonTargets.size(); Loading @@ -2234,15 +2273,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton)) { return; } final String serviceName = serviceInfo.getComponentName().flattenToString(); final String serviceName = componentName.flattenToString(); if (TextUtils.isEmpty(serviceName)) { return; } if (shortcutKeyTargets.contains(serviceName) || buttonTargets.contains(serviceName)) { if (doesShortcutTargetsStringContain(buttonTargets, serviceName) || doesShortcutTargetsStringContain(shortcutKeyTargets, serviceName)) { return; } // For enabled a11y services targeting sdk version > Q and requesting a11y button should // be assigned to a shortcut. Slog.v(LOG_TAG, "A enabled service requesting a11y button " + componentName + " should be assign to the button or shortcut."); buttonTargets.add(serviceName); }); changed |= (lastSize != buttonTargets.size()); Loading @@ -2251,8 +2293,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Update setting key with new value. persistColonDelimitedSetToSettingLocked( Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, buttonTargets, str -> str); scheduleNotifyClientsOfServicesStateChangeLocked(userState); } Loading Loading @@ -2353,12 +2394,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } // In case the caller specified a target name if (targetName != null) { if (!shortcutTargets.contains(targetName)) { Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName); return; if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, targetName)) { Slog.v(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName); targetName = null; } } else { if (targetName == null) { // In case there are many targets assigned to the given shortcut. if (shortcutTargets.size() > 1) { showAccessibilityTargetsSelection(displayId, shortcutType); Loading Loading @@ -2983,6 +3023,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mAccessibilityButtonComponentIdUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT); private final Uri mAccessibilityButtonTargetsUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS); Loading Loading @@ -3015,6 +3058,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAccessibilityButtonTargetsUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( Loading Loading @@ -3061,7 +3106,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub onUserStateChangedLocked(userState); } } else if (mAccessibilityButtonComponentIdUri.equals(uri)) { if (readAccessibilityButtonSettingsLocked(userState)) { if (readAccessibilityButtonTargetComponentLocked(userState)) { onUserStateChangedLocked(userState); } } else if (mAccessibilityButtonTargetsUri.equals(uri)) { if (readAccessibilityButtonTargetsLocked(userState)) { onUserStateChangedLocked(userState); } } else if (mUserNonInteractiveUiTimeoutUri.equals(uri) Loading services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +58 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import com.android.internal.accessibility.AccessibilityShortcutController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; Loading Loading @@ -96,6 +97,8 @@ class AccessibilityUserState { private ComponentName mServiceChangingSoftKeyboardMode; private String mTargetAssignedToAccessibilityButton; private boolean mBindInstantServiceAllowed; private boolean mIsAutoclickEnabled; private boolean mIsDisplayMagnificationEnabled; Loading Loading @@ -152,6 +155,7 @@ class AccessibilityUserState { mTouchExplorationGrantedServices.clear(); mAccessibilityShortcutKeyTargets.clear(); mAccessibilityButtonTargets.clear(); mTargetAssignedToAccessibilityButton = null; mIsTouchExplorationEnabled = false; mServiceHandlesDoubleTap = false; mRequestMultiFingerGestures = false; Loading Loading @@ -469,6 +473,8 @@ class AccessibilityUserState { } } pw.println("}"); pw.append(" button target:{").append(mTargetAssignedToAccessibilityButton); pw.println("}"); pw.append(" Bound services:{"); final int serviceCount = mBoundServices.size(); for (int j = 0; j < serviceCount; j++) { Loading Loading @@ -716,4 +722,56 @@ class AccessibilityUserState { public void setUserNonInteractiveUiTimeoutLocked(int timeout) { mUserNonInteractiveUiTimeout = timeout; } /** * Gets a shortcut target which is assigned to the accessibility button by the chooser * activity. * * @return The flattened component name or the system class name of the shortcut target. */ public String getTargetAssignedToAccessibilityButton() { return mTargetAssignedToAccessibilityButton; } /** * Sets a shortcut target which is assigned to the accessibility button by the chooser * activity. * * @param target The flattened component name or the system class name of the shortcut target. */ public void setTargetAssignedToAccessibilityButton(String target) { mTargetAssignedToAccessibilityButton = target; } /** * Whether or not the given target name is contained in the shortcut collection. Since the * component name string format could be short or long, this function un-flatten the component * name from the string in {@code shortcutTargets} and compared with the given target name. * * @param shortcutTargets The shortcut type. * @param targetName The target name. * @return {@code true} if the target is in the shortcut collection. */ public static boolean doesShortcutTargetsStringContain(Collection<String> shortcutTargets, String targetName) { if (shortcutTargets == null || targetName == null) { return false; } // Some system features, such as magnification, don't have component name. Using string // compare first. if (shortcutTargets.contains(targetName)) { return true; } final ComponentName targetComponentName = ComponentName.unflattenFromString(targetName); if (targetComponentName == null) { return false; } for (String stringName : shortcutTargets) { if (!TextUtils.isEmpty(stringName) && targetComponentName.equals(ComponentName.unflattenFromString(stringName))) { return true; } } return false; } } services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +36 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ 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.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; Loading @@ -41,6 +43,7 @@ import android.content.Context; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; import android.util.ArraySet; import com.android.internal.util.test.FakeSettingsProvider; Loading @@ -56,6 +59,12 @@ public class AccessibilityUserStateTest { private static final ComponentName COMPONENT_NAME = new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest"); private static final ComponentName COMPONENT_NAME1 = new ComponentName("com.android.server.accessibility", "com.android.server.accessibility.AccessibilityUserStateTest1"); private static final ComponentName COMPONENT_NAME2 = new ComponentName("com.android.server.accessibility", "com.android.server.accessibility.AccessibilityUserStateTest2"); // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD private static final int STATE_HIDE_IME = 0; Loading Loading @@ -111,6 +120,7 @@ public class AccessibilityUserStateTest { mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME); mUserState.mAccessibilityShortcutKeyTargets.add(COMPONENT_NAME.flattenToString()); mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString()); mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString()); mUserState.setTouchExplorationEnabledLocked(true); mUserState.setDisplayMagnificationEnabledLocked(true); mUserState.setAutoclickEnabledLocked(true); Loading @@ -129,6 +139,7 @@ public class AccessibilityUserStateTest { assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty()); assertTrue(mUserState.mAccessibilityShortcutKeyTargets.isEmpty()); assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty()); assertNull(mUserState.getTargetAssignedToAccessibilityButton()); assertFalse(mUserState.isTouchExplorationEnabledLocked()); assertFalse(mUserState.isDisplayMagnificationEnabledLocked()); assertFalse(mUserState.isAutoclickEnabledLocked()); Loading Loading @@ -285,6 +296,31 @@ public class AccessibilityUserStateTest { verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN)); } @Test public void doesShortcutTargetsStringContain_returnFalse() { assertFalse(doesShortcutTargetsStringContain(null, null)); assertFalse(doesShortcutTargetsStringContain(null, COMPONENT_NAME.flattenToShortString())); assertFalse(doesShortcutTargetsStringContain(new ArraySet<>(), null)); final ArraySet<String> shortcutTargets = new ArraySet<>(); shortcutTargets.add(COMPONENT_NAME.flattenToString()); assertFalse(doesShortcutTargetsStringContain(shortcutTargets, COMPONENT_NAME1.flattenToString())); } @Test public void isAssignedToShortcutLocked_withDifferentTypeComponentString_returnTrue() { final ArraySet<String> shortcutTargets = new ArraySet<>(); shortcutTargets.add(COMPONENT_NAME1.flattenToShortString()); shortcutTargets.add(COMPONENT_NAME2.flattenToString()); assertTrue(doesShortcutTargetsStringContain(shortcutTargets, COMPONENT_NAME1.flattenToString())); assertTrue(doesShortcutTargetsStringContain(shortcutTargets, COMPONENT_NAME2.flattenToShortString())); } @Test public void isShortcutTargetInstalledLocked_returnTrue() { mUserState.mInstalledServices.add(mMockServiceInfo); Loading Loading
packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +2 −10 Original line number Diff line number Diff line Loading @@ -4692,24 +4692,16 @@ public class SettingsProvider extends ContentProvider { if (currentVersion == 185) { // Deprecate ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, and migrate it // to ACCESSIBILITY_BUTTON_TARGET_COMPONENT. // to ACCESSIBILITY_BUTTON_TARGETS. final SettingsState secureSettings = getSecureSettingsLocked(userId); final Setting magnifyNavbarEnabled = secureSettings.getSettingLocked( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); if ("1".equals(magnifyNavbarEnabled.getValue())) { secureSettings.insertSettingLocked( Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, Secure.ACCESSIBILITY_BUTTON_TARGETS, ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER, null /* tag */, false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME); } else { // Clear a11y button targets list setting. A11yManagerService will end up // adding all legacy enabled services that want the button to the list, so // there's no need to keep tracking them. secureSettings.insertSettingLocked( Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, null, null /* tag */, false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME); } secureSettings.deleteSettingLocked( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); Loading
services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +1 −0 Original line number Diff line number Diff line Loading @@ -1081,6 +1081,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ pw.append(", eventTypes=" + AccessibilityEvent.eventTypeToString(mEventTypes)); pw.append(", notificationTimeout=" + mNotificationTimeout); pw.append(", requestA11yBtn=" + mRequestAccessibilityButton); pw.append("]"); } } Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +72 −23 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.view.accessibility.AccessibilityManager.ShortcutType; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; import android.Manifest; import android.accessibilityservice.AccessibilityGestureEvent; Loading Loading @@ -876,6 +877,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub throw new SecurityException("Caller does not hold permission " + android.Manifest.permission.STATUS_BAR_SERVICE); } if (targetName == null) { synchronized (mLock) { final AccessibilityUserState userState = getCurrentUserStateLocked(); targetName = userState.getTargetAssignedToAccessibilityButton(); } } mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::performAccessibilityShortcutInternal, this, displayId, ACCESSIBILITY_BUTTON, targetName)); Loading Loading @@ -1828,7 +1835,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readMagnificationEnabledSettingsLocked(userState); somethingChanged |= readAutoclickEnabledSettingLocked(userState); somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState); somethingChanged |= readAccessibilityButtonSettingsLocked(userState); somethingChanged |= readAccessibilityButtonTargetsLocked(userState); somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState); somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState); return somethingChanged; } Loading Loading @@ -1948,9 +1956,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) { private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) { final Set<String> targetsFromSetting = new ArraySet<>(); readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, targetsFromSetting, str -> str); final Set<String> currentTargets = Loading @@ -1964,6 +1972,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) { final String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId); if (TextUtils.isEmpty(componentId)) { if (userState.getTargetAssignedToAccessibilityButton() == null) { return false; } userState.setTargetAssignedToAccessibilityButton(null); return true; } if (componentId.equals(userState.getTargetAssignedToAccessibilityButton())) { return false; } userState.setTargetAssignedToAccessibilityButton(componentId); return true; } private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) { final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser( mContext.getContentResolver(), Loading @@ -1984,7 +2009,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } /** * Check if the targets that will be enabled by the accessibility shortcut key is installed. * Check if the target that will be enabled by the accessibility shortcut key is installed. * If it isn't, remove it from the list and associated setting so a side loaded service can't * spoof the package name of the default service. */ Loading Loading @@ -2145,7 +2170,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * 1) Update accessibility button availability to accessibility services. * 2) Check if the targets that will be enabled by the accessibility button is installed. * 2) Check if the target that will be enabled by the accessibility button is installed. * If it isn't, remove it from the list and associated setting so a side loaded service can't * spoof the package name of the default service. */ Loading @@ -2172,8 +2197,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Update setting key with new value. persistColonDelimitedSetToSettingLocked( Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, currentTargets, str -> str); scheduleNotifyClientsOfServicesStateChangeLocked(userState); } Loading @@ -2182,7 +2206,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * 1) Check if the service assigned to accessibility button target sdk version > Q. * If it isn't, remove it from the list and associated setting. * (It happens when an accessibility service package is downgraded.) * 2) Check if an enabled service targeting sdk version > Q and requesting a11y button is * 2) For a service targeting sdk version > Q and requesting a11y button, it should be in the * enabled list if's assigned to a11y button. * (It happens when an accessibility service package is same graded, and updated requesting * a11y button flag) * 3) Check if an enabled service targeting sdk version > Q and requesting a11y button is * assigned to a shortcut. If it isn't, assigns it to the accessibility button. * (It happens when an enabled accessibility service package is upgraded.) * Loading @@ -2207,11 +2235,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo .targetSdkVersion > Build.VERSION_CODES.Q) { return false; } .targetSdkVersion <= Build.VERSION_CODES.Q) { // A11y services targeting sdk version <= Q should not be in the list. Slog.v(LOG_TAG, "Legacy service " + componentName + " should not in the button"); return true; } final boolean requestA11yButton = (serviceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; if (requestA11yButton && !userState.mEnabledServices.contains(componentName)) { // An a11y service targeting sdk version > Q and request A11y button and is assigned // to a11y btn should be in the enabled list. Slog.v(LOG_TAG, "Service requesting a11y button and be assigned to the button" + componentName + " should be enabled state"); return true; } return false; }); boolean changed = (lastSize != buttonTargets.size()); lastSize = buttonTargets.size(); Loading @@ -2234,15 +2273,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton)) { return; } final String serviceName = serviceInfo.getComponentName().flattenToString(); final String serviceName = componentName.flattenToString(); if (TextUtils.isEmpty(serviceName)) { return; } if (shortcutKeyTargets.contains(serviceName) || buttonTargets.contains(serviceName)) { if (doesShortcutTargetsStringContain(buttonTargets, serviceName) || doesShortcutTargetsStringContain(shortcutKeyTargets, serviceName)) { return; } // For enabled a11y services targeting sdk version > Q and requesting a11y button should // be assigned to a shortcut. Slog.v(LOG_TAG, "A enabled service requesting a11y button " + componentName + " should be assign to the button or shortcut."); buttonTargets.add(serviceName); }); changed |= (lastSize != buttonTargets.size()); Loading @@ -2251,8 +2293,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Update setting key with new value. persistColonDelimitedSetToSettingLocked( Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, buttonTargets, str -> str); scheduleNotifyClientsOfServicesStateChangeLocked(userState); } Loading Loading @@ -2353,12 +2394,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } // In case the caller specified a target name if (targetName != null) { if (!shortcutTargets.contains(targetName)) { Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName); return; if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, targetName)) { Slog.v(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName); targetName = null; } } else { if (targetName == null) { // In case there are many targets assigned to the given shortcut. if (shortcutTargets.size() > 1) { showAccessibilityTargetsSelection(displayId, shortcutType); Loading Loading @@ -2983,6 +3023,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mAccessibilityButtonComponentIdUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT); private final Uri mAccessibilityButtonTargetsUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS); Loading Loading @@ -3015,6 +3058,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAccessibilityButtonTargetsUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( Loading Loading @@ -3061,7 +3106,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub onUserStateChangedLocked(userState); } } else if (mAccessibilityButtonComponentIdUri.equals(uri)) { if (readAccessibilityButtonSettingsLocked(userState)) { if (readAccessibilityButtonTargetComponentLocked(userState)) { onUserStateChangedLocked(userState); } } else if (mAccessibilityButtonTargetsUri.equals(uri)) { if (readAccessibilityButtonTargetsLocked(userState)) { onUserStateChangedLocked(userState); } } else if (mUserNonInteractiveUiTimeoutUri.equals(uri) Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +58 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import com.android.internal.accessibility.AccessibilityShortcutController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; Loading Loading @@ -96,6 +97,8 @@ class AccessibilityUserState { private ComponentName mServiceChangingSoftKeyboardMode; private String mTargetAssignedToAccessibilityButton; private boolean mBindInstantServiceAllowed; private boolean mIsAutoclickEnabled; private boolean mIsDisplayMagnificationEnabled; Loading Loading @@ -152,6 +155,7 @@ class AccessibilityUserState { mTouchExplorationGrantedServices.clear(); mAccessibilityShortcutKeyTargets.clear(); mAccessibilityButtonTargets.clear(); mTargetAssignedToAccessibilityButton = null; mIsTouchExplorationEnabled = false; mServiceHandlesDoubleTap = false; mRequestMultiFingerGestures = false; Loading Loading @@ -469,6 +473,8 @@ class AccessibilityUserState { } } pw.println("}"); pw.append(" button target:{").append(mTargetAssignedToAccessibilityButton); pw.println("}"); pw.append(" Bound services:{"); final int serviceCount = mBoundServices.size(); for (int j = 0; j < serviceCount; j++) { Loading Loading @@ -716,4 +722,56 @@ class AccessibilityUserState { public void setUserNonInteractiveUiTimeoutLocked(int timeout) { mUserNonInteractiveUiTimeout = timeout; } /** * Gets a shortcut target which is assigned to the accessibility button by the chooser * activity. * * @return The flattened component name or the system class name of the shortcut target. */ public String getTargetAssignedToAccessibilityButton() { return mTargetAssignedToAccessibilityButton; } /** * Sets a shortcut target which is assigned to the accessibility button by the chooser * activity. * * @param target The flattened component name or the system class name of the shortcut target. */ public void setTargetAssignedToAccessibilityButton(String target) { mTargetAssignedToAccessibilityButton = target; } /** * Whether or not the given target name is contained in the shortcut collection. Since the * component name string format could be short or long, this function un-flatten the component * name from the string in {@code shortcutTargets} and compared with the given target name. * * @param shortcutTargets The shortcut type. * @param targetName The target name. * @return {@code true} if the target is in the shortcut collection. */ public static boolean doesShortcutTargetsStringContain(Collection<String> shortcutTargets, String targetName) { if (shortcutTargets == null || targetName == null) { return false; } // Some system features, such as magnification, don't have component name. Using string // compare first. if (shortcutTargets.contains(targetName)) { return true; } final ComponentName targetComponentName = ComponentName.unflattenFromString(targetName); if (targetComponentName == null) { return false; } for (String stringName : shortcutTargets) { if (!TextUtils.isEmpty(stringName) && targetComponentName.equals(ComponentName.unflattenFromString(stringName))) { return true; } } return false; } }
services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +36 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ 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.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; Loading @@ -41,6 +43,7 @@ import android.content.Context; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; import android.util.ArraySet; import com.android.internal.util.test.FakeSettingsProvider; Loading @@ -56,6 +59,12 @@ public class AccessibilityUserStateTest { private static final ComponentName COMPONENT_NAME = new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest"); private static final ComponentName COMPONENT_NAME1 = new ComponentName("com.android.server.accessibility", "com.android.server.accessibility.AccessibilityUserStateTest1"); private static final ComponentName COMPONENT_NAME2 = new ComponentName("com.android.server.accessibility", "com.android.server.accessibility.AccessibilityUserStateTest2"); // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD private static final int STATE_HIDE_IME = 0; Loading Loading @@ -111,6 +120,7 @@ public class AccessibilityUserStateTest { mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME); mUserState.mAccessibilityShortcutKeyTargets.add(COMPONENT_NAME.flattenToString()); mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString()); mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString()); mUserState.setTouchExplorationEnabledLocked(true); mUserState.setDisplayMagnificationEnabledLocked(true); mUserState.setAutoclickEnabledLocked(true); Loading @@ -129,6 +139,7 @@ public class AccessibilityUserStateTest { assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty()); assertTrue(mUserState.mAccessibilityShortcutKeyTargets.isEmpty()); assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty()); assertNull(mUserState.getTargetAssignedToAccessibilityButton()); assertFalse(mUserState.isTouchExplorationEnabledLocked()); assertFalse(mUserState.isDisplayMagnificationEnabledLocked()); assertFalse(mUserState.isAutoclickEnabledLocked()); Loading Loading @@ -285,6 +296,31 @@ public class AccessibilityUserStateTest { verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN)); } @Test public void doesShortcutTargetsStringContain_returnFalse() { assertFalse(doesShortcutTargetsStringContain(null, null)); assertFalse(doesShortcutTargetsStringContain(null, COMPONENT_NAME.flattenToShortString())); assertFalse(doesShortcutTargetsStringContain(new ArraySet<>(), null)); final ArraySet<String> shortcutTargets = new ArraySet<>(); shortcutTargets.add(COMPONENT_NAME.flattenToString()); assertFalse(doesShortcutTargetsStringContain(shortcutTargets, COMPONENT_NAME1.flattenToString())); } @Test public void isAssignedToShortcutLocked_withDifferentTypeComponentString_returnTrue() { final ArraySet<String> shortcutTargets = new ArraySet<>(); shortcutTargets.add(COMPONENT_NAME1.flattenToShortString()); shortcutTargets.add(COMPONENT_NAME2.flattenToString()); assertTrue(doesShortcutTargetsStringContain(shortcutTargets, COMPONENT_NAME1.flattenToString())); assertTrue(doesShortcutTargetsStringContain(shortcutTargets, COMPONENT_NAME2.flattenToShortString())); } @Test public void isShortcutTargetInstalledLocked_returnTrue() { mUserState.mInstalledServices.add(mMockServiceInfo); Loading