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

Commit 2d2c9fbd authored by Riley Jones's avatar Riley Jones Committed by Android (Google) Code Review
Browse files

Merge "A11yManagerService turns off button targets for services whose packages...

Merge "A11yManagerService turns off button targets for services whose packages have forcibly stopped." into main
parents a2e32b2e 0fb119c6
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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"
+121 −20
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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();
@@ -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;
@@ -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) {
@@ -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();
@@ -889,6 +976,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
                        return false;
                    }
                }
            }
        };

        // package changes
@@ -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);
@@ -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);
@@ -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.
@@ -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)) {
+70 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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.