Loading services/accessibility/accessibility.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,13 @@ flag { bug: "295327792" } flag { name: "disable_continuous_shortcut_on_force_stop" namespace: "accessibility" description: "When a package is force stopped, remove the button shortcuts of any continuously-running shortcuts." bug: "198018180" } flag { name: "deprecate_package_list_observer" namespace: "accessibility" Loading services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +121 −20 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT; Loading Loading @@ -182,6 +183,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.function.Consumer; Loading Loading @@ -650,6 +652,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } /** * Returns the lock object for any synchronized test blocks. * Should not be used outside of testing. * @return lock object. */ @VisibleForTesting Object getLock() { return mLock; } AccessibilityUserState getCurrentUserState() { synchronized (mLock) { return getCurrentUserStateLocked(); Loading Loading @@ -746,6 +758,62 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } /** * Handles a package or packages being force stopped. * Will disable any relevant services, * and remove any button targets of continuous services, * denoted by {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON}. * If the result is {@code true}, * then {@link AccessibilityManagerService#onUserStateChangedLocked( * AccessibilityUserState, boolean)} should be called afterwards. * * @param packages list of packages that have stopped. * @param userState user state to be read & modified. * @return {@code true} if a service was enabled or a button target was removed, * {@code false} otherwise. */ @VisibleForTesting boolean onPackagesForceStoppedLocked( String[] packages, AccessibilityUserState userState) { final List<String> continuousServicePackages = userState.mInstalledServices.stream().filter(service -> (service.flags & FLAG_REQUEST_ACCESSIBILITY_BUTTON) == FLAG_REQUEST_ACCESSIBILITY_BUTTON ).map(service -> service.getComponentName().flattenToString()).toList(); boolean enabledServicesChanged = false; final Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { final ComponentName comp = it.next(); final String compPkg = comp.getPackageName(); for (String pkg : packages) { if (compPkg.equals(pkg)) { it.remove(); userState.getBindingServicesLocked().remove(comp); userState.getCrashedServicesLocked().remove(comp); enabledServicesChanged = true; } } } if (enabledServicesChanged) { persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mEnabledServices, userState.mUserId); } boolean buttonTargetsChanged = userState.mAccessibilityButtonTargets.removeIf( target -> continuousServicePackages.stream().anyMatch( pkg -> Objects.equals(target, pkg))); if (buttonTargetsChanged) { persistColonDelimitedSetToSettingLocked( Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, userState.mAccessibilityButtonTargets, str -> str); } return enabledServicesChanged || buttonTargetsChanged; } @VisibleForTesting PackageMonitor getPackageMonitor() { return mPackageMonitor; Loading Loading @@ -850,6 +918,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } /** * Handles instances in which a package or packages have forcibly stopped. * * @param intent intent containing package event information. * @param uid linux process user id (different from Android user id). * @param packages array of package names that have stopped. * @param doit whether to try and handle the stop or just log the trace. * * @return {@code true} if package should be restarted, {@code false} otherwise. */ @Override public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { Loading @@ -867,6 +945,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } final AccessibilityUserState userState = getUserStateLocked(userId); if (Flags.disableContinuousShortcutOnForceStop()) { if (doit && onPackagesForceStoppedLocked(packages, userState)) { onUserStateChangedLocked(userState); return false; } else { return true; } } else { final Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { final ComponentName comp = it.next(); Loading @@ -889,6 +976,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } } } }; // package changes Loading Loading @@ -2452,7 +2540,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * @param userId The user id. * @param outComponentNames The output component names. */ private void readComponentNamesFromSettingLocked(String settingName, int userId, @VisibleForTesting void readComponentNamesFromSettingLocked(String settingName, int userId, Set<ComponentName> outComponentNames) { readColonDelimitedSettingToSet(settingName, userId, str -> ComponentName.unflattenFromString(str), outComponentNames); Loading Loading @@ -2481,7 +2570,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub componentName -> componentName.flattenToShortString()); } private <T> void readColonDelimitedSettingToSet(String settingName, int userId, /** * Reads a colon delimited setting, * passes the values through a function, * then stores the values in a provided set. * * @param settingName Name of setting. * @param userId user id corresponding to setting. * @param toItem function mapping values to the output set. * @param outSet output set to write to. * @param <T> type of output set. */ @VisibleForTesting <T> void readColonDelimitedSettingToSet(String settingName, int userId, Function<String, T> toItem, Set<T> outSet) { final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), settingName, userId); Loading Loading @@ -3475,7 +3576,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } final boolean requestA11yButton = (serviceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; & 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. Loading Loading @@ -3776,7 +3877,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final int targetSdk = installedServiceInfo.getResolveInfo() .serviceInfo.applicationInfo.targetSdkVersion; final boolean requestA11yButton = (installedServiceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; // Turns on / off the accessibility service if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY) || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) { Loading services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; Loading Loading @@ -68,6 +69,7 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.TestableContext; import android.util.ArraySet; import android.view.Display; import android.view.DisplayAdjustments; import android.view.DisplayInfo; Loading Loading @@ -599,6 +601,74 @@ public class AccessibilityManagerServiceTest { ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()); } @Test public void testPackagesForceStopped_disablesRelevantService() { final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo(); info_a.setComponentName(COMPONENT_NAME); final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo(); info_b.setComponentName(new ComponentName("package", "class")); AccessibilityUserState userState = mA11yms.getCurrentUserState(); userState.mInstalledServices.clear(); userState.mInstalledServices.add(info_a); userState.mInstalledServices.add(info_b); userState.mEnabledServices.clear(); userState.mEnabledServices.add(info_a.getComponentName()); userState.mEnabledServices.add(info_b.getComponentName()); synchronized (mA11yms.getLock()) { mA11yms.onPackagesForceStoppedLocked( new String[]{info_a.getComponentName().getPackageName()}, userState); } //Assert user state change userState = mA11yms.getCurrentUserState(); assertThat(userState.mEnabledServices).containsExactly(info_b.getComponentName()); //Assert setting change final Set<ComponentName> componentsFromSetting = new ArraySet<>(); mA11yms.readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mUserId, componentsFromSetting); assertThat(componentsFromSetting).containsExactly(info_b.getComponentName()); } @Test @RequiresFlagsEnabled(Flags.FLAG_DISABLE_CONTINUOUS_SHORTCUT_ON_FORCE_STOP) public void testPackagesForceStopped_fromContinuousService_removesButtonTarget() { final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo(); info_a.setComponentName(COMPONENT_NAME); info_a.flags = FLAG_REQUEST_ACCESSIBILITY_BUTTON; final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo(); info_b.setComponentName(new ComponentName("package", "class")); AccessibilityUserState userState = mA11yms.getCurrentUserState(); userState.mInstalledServices.clear(); userState.mInstalledServices.add(info_a); userState.mInstalledServices.add(info_b); userState.mAccessibilityButtonTargets.clear(); userState.mAccessibilityButtonTargets.add(info_a.getComponentName().flattenToString()); userState.mAccessibilityButtonTargets.add(info_b.getComponentName().flattenToString()); // despite force stopping both packages, only the first service has the relevant flag, // so only the first should be removed. synchronized (mA11yms.getLock()) { mA11yms.onPackagesForceStoppedLocked( new String[]{ info_a.getComponentName().getPackageName(), info_b.getComponentName().getPackageName()}, userState); } //Assert user state change userState = mA11yms.getCurrentUserState(); assertThat(userState.mAccessibilityButtonTargets).containsExactly( info_b.getComponentName().flattenToString()); //Assert setting change final Set<String> targetsFromSetting = new ArraySet<>(); mA11yms.readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, str -> str, targetsFromSetting); assertThat(targetsFromSetting).containsExactly(info_b.getComponentName().flattenToString()); } @Test @RequiresFlagsDisabled(Flags.FLAG_SCAN_PACKAGES_WITHOUT_LOCK) // Test old behavior to validate lock detection for the old (locked access) case. Loading Loading
services/accessibility/accessibility.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,13 @@ flag { bug: "295327792" } flag { name: "disable_continuous_shortcut_on_force_stop" namespace: "accessibility" description: "When a package is force stopped, remove the button shortcuts of any continuously-running shortcuts." bug: "198018180" } flag { name: "deprecate_package_list_observer" namespace: "accessibility" Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +121 −20 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT; Loading Loading @@ -182,6 +183,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.function.Consumer; Loading Loading @@ -650,6 +652,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } /** * Returns the lock object for any synchronized test blocks. * Should not be used outside of testing. * @return lock object. */ @VisibleForTesting Object getLock() { return mLock; } AccessibilityUserState getCurrentUserState() { synchronized (mLock) { return getCurrentUserStateLocked(); Loading Loading @@ -746,6 +758,62 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } /** * Handles a package or packages being force stopped. * Will disable any relevant services, * and remove any button targets of continuous services, * denoted by {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON}. * If the result is {@code true}, * then {@link AccessibilityManagerService#onUserStateChangedLocked( * AccessibilityUserState, boolean)} should be called afterwards. * * @param packages list of packages that have stopped. * @param userState user state to be read & modified. * @return {@code true} if a service was enabled or a button target was removed, * {@code false} otherwise. */ @VisibleForTesting boolean onPackagesForceStoppedLocked( String[] packages, AccessibilityUserState userState) { final List<String> continuousServicePackages = userState.mInstalledServices.stream().filter(service -> (service.flags & FLAG_REQUEST_ACCESSIBILITY_BUTTON) == FLAG_REQUEST_ACCESSIBILITY_BUTTON ).map(service -> service.getComponentName().flattenToString()).toList(); boolean enabledServicesChanged = false; final Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { final ComponentName comp = it.next(); final String compPkg = comp.getPackageName(); for (String pkg : packages) { if (compPkg.equals(pkg)) { it.remove(); userState.getBindingServicesLocked().remove(comp); userState.getCrashedServicesLocked().remove(comp); enabledServicesChanged = true; } } } if (enabledServicesChanged) { persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mEnabledServices, userState.mUserId); } boolean buttonTargetsChanged = userState.mAccessibilityButtonTargets.removeIf( target -> continuousServicePackages.stream().anyMatch( pkg -> Objects.equals(target, pkg))); if (buttonTargetsChanged) { persistColonDelimitedSetToSettingLocked( Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, userState.mAccessibilityButtonTargets, str -> str); } return enabledServicesChanged || buttonTargetsChanged; } @VisibleForTesting PackageMonitor getPackageMonitor() { return mPackageMonitor; Loading Loading @@ -850,6 +918,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } /** * Handles instances in which a package or packages have forcibly stopped. * * @param intent intent containing package event information. * @param uid linux process user id (different from Android user id). * @param packages array of package names that have stopped. * @param doit whether to try and handle the stop or just log the trace. * * @return {@code true} if package should be restarted, {@code false} otherwise. */ @Override public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { Loading @@ -867,6 +945,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } final AccessibilityUserState userState = getUserStateLocked(userId); if (Flags.disableContinuousShortcutOnForceStop()) { if (doit && onPackagesForceStoppedLocked(packages, userState)) { onUserStateChangedLocked(userState); return false; } else { return true; } } else { final Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { final ComponentName comp = it.next(); Loading @@ -889,6 +976,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } } } }; // package changes Loading Loading @@ -2452,7 +2540,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * @param userId The user id. * @param outComponentNames The output component names. */ private void readComponentNamesFromSettingLocked(String settingName, int userId, @VisibleForTesting void readComponentNamesFromSettingLocked(String settingName, int userId, Set<ComponentName> outComponentNames) { readColonDelimitedSettingToSet(settingName, userId, str -> ComponentName.unflattenFromString(str), outComponentNames); Loading Loading @@ -2481,7 +2570,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub componentName -> componentName.flattenToShortString()); } private <T> void readColonDelimitedSettingToSet(String settingName, int userId, /** * Reads a colon delimited setting, * passes the values through a function, * then stores the values in a provided set. * * @param settingName Name of setting. * @param userId user id corresponding to setting. * @param toItem function mapping values to the output set. * @param outSet output set to write to. * @param <T> type of output set. */ @VisibleForTesting <T> void readColonDelimitedSettingToSet(String settingName, int userId, Function<String, T> toItem, Set<T> outSet) { final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), settingName, userId); Loading Loading @@ -3475,7 +3576,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } final boolean requestA11yButton = (serviceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; & 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. Loading Loading @@ -3776,7 +3877,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final int targetSdk = installedServiceInfo.getResolveInfo() .serviceInfo.applicationInfo.targetSdkVersion; final boolean requestA11yButton = (installedServiceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; // Turns on / off the accessibility service if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY) || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) { Loading
services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; Loading Loading @@ -68,6 +69,7 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.TestableContext; import android.util.ArraySet; import android.view.Display; import android.view.DisplayAdjustments; import android.view.DisplayInfo; Loading Loading @@ -599,6 +601,74 @@ public class AccessibilityManagerServiceTest { ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()); } @Test public void testPackagesForceStopped_disablesRelevantService() { final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo(); info_a.setComponentName(COMPONENT_NAME); final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo(); info_b.setComponentName(new ComponentName("package", "class")); AccessibilityUserState userState = mA11yms.getCurrentUserState(); userState.mInstalledServices.clear(); userState.mInstalledServices.add(info_a); userState.mInstalledServices.add(info_b); userState.mEnabledServices.clear(); userState.mEnabledServices.add(info_a.getComponentName()); userState.mEnabledServices.add(info_b.getComponentName()); synchronized (mA11yms.getLock()) { mA11yms.onPackagesForceStoppedLocked( new String[]{info_a.getComponentName().getPackageName()}, userState); } //Assert user state change userState = mA11yms.getCurrentUserState(); assertThat(userState.mEnabledServices).containsExactly(info_b.getComponentName()); //Assert setting change final Set<ComponentName> componentsFromSetting = new ArraySet<>(); mA11yms.readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mUserId, componentsFromSetting); assertThat(componentsFromSetting).containsExactly(info_b.getComponentName()); } @Test @RequiresFlagsEnabled(Flags.FLAG_DISABLE_CONTINUOUS_SHORTCUT_ON_FORCE_STOP) public void testPackagesForceStopped_fromContinuousService_removesButtonTarget() { final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo(); info_a.setComponentName(COMPONENT_NAME); info_a.flags = FLAG_REQUEST_ACCESSIBILITY_BUTTON; final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo(); info_b.setComponentName(new ComponentName("package", "class")); AccessibilityUserState userState = mA11yms.getCurrentUserState(); userState.mInstalledServices.clear(); userState.mInstalledServices.add(info_a); userState.mInstalledServices.add(info_b); userState.mAccessibilityButtonTargets.clear(); userState.mAccessibilityButtonTargets.add(info_a.getComponentName().flattenToString()); userState.mAccessibilityButtonTargets.add(info_b.getComponentName().flattenToString()); // despite force stopping both packages, only the first service has the relevant flag, // so only the first should be removed. synchronized (mA11yms.getLock()) { mA11yms.onPackagesForceStoppedLocked( new String[]{ info_a.getComponentName().getPackageName(), info_b.getComponentName().getPackageName()}, userState); } //Assert user state change userState = mA11yms.getCurrentUserState(); assertThat(userState.mAccessibilityButtonTargets).containsExactly( info_b.getComponentName().flattenToString()); //Assert setting change final Set<String> targetsFromSetting = new ArraySet<>(); mA11yms.readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, str -> str, targetsFromSetting); assertThat(targetsFromSetting).containsExactly(info_b.getComponentName().flattenToString()); } @Test @RequiresFlagsDisabled(Flags.FLAG_SCAN_PACKAGES_WITHOUT_LOCK) // Test old behavior to validate lock detection for the old (locked access) case. Loading