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

Commit 7c2681d9 authored by Daniel Norman's avatar Daniel Norman Committed by Android Build Coastguard Worker
Browse files

Stops hiding a11y services with the same package+label as an activity.

Bug: 353700779
Test: atest AccessibilitySettings, ensure new test passes
Test: revert AccessibilitySettings.java change, ensure new test fails
Test: Install poc APKs from the bug, observe issue not reproducible
Flag: NONE security fix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c38fd822ba27224c179b80de2b89a1e9ab8bee59)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ccc0d9d0ebace1cf0925b7c696bc41d6cf9efe52)
Merged-In: Ie680e80169aa734f2559fe50ef06e4d1eae46779
Change-Id: Ie680e80169aa734f2559fe50ef06e4d1eae46779
parent dcf8f310
Loading
Loading
Loading
Loading
+1 −26
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.accessibilityservice.AccessibilityShortcutInfo;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
@@ -29,7 +28,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager;

@@ -57,8 +55,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/** Activity with the accessibility settings. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -458,20 +454,9 @@ public class AccessibilitySettings extends DashboardFragment implements
                        UserHandle.myUserId());
        final List<AccessibilityActivityPreference> activityList =
                preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList);
        final Set<Pair<String, CharSequence>> packageLabelPairs =
                activityList.stream()
                        .map(a11yActivityPref -> new Pair<>(
                                a11yActivityPref.getPackageName(), a11yActivityPref.getLabel())
                        ).collect(Collectors.toSet());

        // Remove duplicate item here, new a ArrayList to copy unmodifiable list result
        // (getInstalledAccessibilityServiceList).

        final List<AccessibilityServiceInfo> installedServiceList = new ArrayList<>(
                a11yManager.getInstalledAccessibilityServiceList());
        if (!packageLabelPairs.isEmpty()) {
            installedServiceList.removeIf(
                    target -> containsPackageAndLabelInList(packageLabelPairs, target));
        }
        final List<RestrictedPreference> serviceList =
                preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList);

@@ -482,16 +467,6 @@ public class AccessibilitySettings extends DashboardFragment implements
        return preferenceList;
    }

    private boolean containsPackageAndLabelInList(
            Set<Pair<String, CharSequence>> packageLabelPairs,
            AccessibilityServiceInfo targetServiceInfo) {
        final ServiceInfo serviceInfo = targetServiceInfo.getResolveInfo().serviceInfo;
        final String servicePackageName = serviceInfo.packageName;
        final CharSequence serviceLabel = serviceInfo.loadLabel(getPackageManager());

        return packageLabelPairs.contains(new Pair<>(servicePackageName, serviceLabel));
    }

    private void initializePreBundledServicesMapFromArray(String categoryKey, int key) {
        String[] services = getResources().getStringArray(key);
        PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
+47 −17
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
@@ -50,6 +51,7 @@ import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
@@ -75,7 +77,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.shadows.ShadowContentResolver;
import org.xmlpull.v1.XmlPullParserException;

@@ -87,6 +88,7 @@ import java.util.List;
/** Test for {@link AccessibilitySettings}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
        ShadowAccessibilityManager.class,
        ShadowBluetoothAdapter.class,
        ShadowUserManager.class,
        ShadowColorDisplayManager.class,
@@ -95,8 +97,10 @@ import java.util.List;
})
public class AccessibilitySettingsTest {
    private static final String PACKAGE_NAME = "com.android.test";
    private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service";
    private static final ComponentName COMPONENT_NAME = new ComponentName(PACKAGE_NAME, CLASS_NAME);
    private static final ComponentName SERVICE_COMPONENT_NAME =
            new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_service");
    private static final ComponentName ACTIVITY_COMPONENT_NAME =
            new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_activity");
    private static final String EMPTY_STRING = "";
    private static final String DEFAULT_SUMMARY = "default summary";
    private static final String DEFAULT_DESCRIPTION = "default description";
@@ -110,9 +114,7 @@ public class AccessibilitySettingsTest {
    private final Context mContext = ApplicationProvider.getApplicationContext();
    @Spy
    private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
            PACKAGE_NAME, CLASS_NAME);
    @Mock
    private AccessibilityShortcutInfo mShortcutInfo;
            SERVICE_COMPONENT_NAME);
    private ShadowAccessibilityManager mShadowAccessibilityManager;
    @Mock
    private LocalBluetoothManager mLocalBluetoothManager;
@@ -121,11 +123,11 @@ public class AccessibilitySettingsTest {

    @Before
    public void setup() {
        mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext));
        mShadowAccessibilityManager = Shadow.extract(
                mContext.getSystemService(AccessibilityManager.class));
        mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
        mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
        setMockAccessibilityShortcutInfo(mShortcutInfo);

        Intent intent = new Intent();
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
@@ -368,7 +370,7 @@ public class AccessibilitySettingsTest {
        mFragment.onContentChanged();

        RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
                COMPONENT_NAME.flattenToString());
                SERVICE_COMPONENT_NAME.flattenToString());

        assertThat(preference).isNotNull();

@@ -388,7 +390,7 @@ public class AccessibilitySettingsTest {
        mFragment.onResume();

        RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
                COMPONENT_NAME.flattenToString());
                SERVICE_COMPONENT_NAME.flattenToString());

        assertThat(preference).isNotNull();

@@ -418,18 +420,44 @@ public class AccessibilitySettingsTest {
        assertThat(pref).isNull();
    }

    private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
            String className) {
        return getMockAccessibilityServiceInfo(new ComponentName(packageName, className));
    @Test
    public void testSameNamedServiceAndActivity_bothPreferencesExist() {
        final PackageManager pm = mContext.getPackageManager();
        AccessibilityServiceInfo a11yServiceInfo = mServiceInfo;
        AccessibilityShortcutInfo a11yShortcutInfo = getMockAccessibilityShortcutInfo();
        // Ensure the test service and activity have the same package name and label.
        // Before this change, any service and activity with the same package name and
        // label would cause the service to be hidden.
        assertThat(a11yServiceInfo.getComponentName())
                .isNotEqualTo(a11yShortcutInfo.getComponentName());
        assertThat(a11yServiceInfo.getComponentName().getPackageName())
                .isEqualTo(a11yShortcutInfo.getComponentName().getPackageName());
        assertThat(a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(pm))
                .isEqualTo(a11yShortcutInfo.getActivityInfo().loadLabel(pm));
        // Prepare A11yManager with the test service and activity.
        mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
                List.of(mServiceInfo));
        mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
                List.of(getMockAccessibilityShortcutInfo()));
        setupFragment();

        // Both service and activity preferences should exist on the page.
        RestrictedPreference servicePref = mFragment.getPreferenceScreen().findPreference(
                a11yServiceInfo.getComponentName().flattenToString());
        RestrictedPreference activityPref = mFragment.getPreferenceScreen().findPreference(
                a11yShortcutInfo.getComponentName().flattenToString());
        assertThat(servicePref).isNotNull();
        assertThat(activityPref).isNotNull();
    }

    private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) {
        final ApplicationInfo applicationInfo = new ApplicationInfo();
        final ServiceInfo serviceInfo = new ServiceInfo();
        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
        final ServiceInfo serviceInfo = Mockito.spy(new ServiceInfo());
        applicationInfo.packageName = componentName.getPackageName();
        serviceInfo.packageName = componentName.getPackageName();
        serviceInfo.name = componentName.getClassName();
        serviceInfo.applicationInfo = applicationInfo;
        when(serviceInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);

        final ResolveInfo resolveInfo = new ResolveInfo();
        resolveInfo.serviceInfo = serviceInfo;
@@ -445,14 +473,16 @@ public class AccessibilitySettingsTest {
        return null;
    }

    private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) {
    private AccessibilityShortcutInfo getMockAccessibilityShortcutInfo() {
        AccessibilityShortcutInfo mockInfo = Mockito.mock(AccessibilityShortcutInfo.class);
        final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class);
        activityInfo.applicationInfo = new ApplicationInfo();
        when(mockInfo.getActivityInfo()).thenReturn(activityInfo);
        when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
        when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY);
        when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION);
        when(mockInfo.getComponentName()).thenReturn(COMPONENT_NAME);
        when(mockInfo.getComponentName()).thenReturn(ACTIVITY_COMPONENT_NAME);
        return mockInfo;
    }

    private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {
+23 −1
Original line number Diff line number Diff line
@@ -16,15 +16,18 @@

package com.android.settings.testutils.shadow;

import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;

import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;

import java.util.List;
import java.util.Map;

/**
@@ -33,9 +36,10 @@ import java.util.Map;
@Implements(AccessibilityManager.class)
public class ShadowAccessibilityManager extends org.robolectric.shadows.ShadowAccessibilityManager {
    private Map<ComponentName, ComponentName> mA11yFeatureToTileMap = new ArrayMap<>();
    private List<AccessibilityShortcutInfo> mInstalledAccessibilityShortcutList = List.of();

    /**
     * Implements a hidden method {@link AccessibilityManager.getA11yFeatureToTileMap}
     * Implements a hidden method {@link AccessibilityManager#getA11yFeatureToTileMap}
     */
    @Implementation
    public Map<ComponentName, ComponentName> getA11yFeatureToTileMap(@UserIdInt int userId) {
@@ -49,4 +53,22 @@ public class ShadowAccessibilityManager extends org.robolectric.shadows.ShadowAc
            @NonNull Map<ComponentName, ComponentName> a11yFeatureToTileMap) {
        mA11yFeatureToTileMap = a11yFeatureToTileMap;
    }

    /**
     * Implements the hidden method
     * {@link AccessibilityManager#getInstalledAccessibilityShortcutListAsUser}.
     */
    @Implementation
    public List<AccessibilityShortcutInfo> getInstalledAccessibilityShortcutListAsUser(
            @NonNull Context context, @UserIdInt int userId) {
        return mInstalledAccessibilityShortcutList;
    }

    /**
     * Sets the value to be returned by {@link #getInstalledAccessibilityShortcutListAsUser}.
     */
    public void setInstalledAccessibilityShortcutListAsUser(
            @NonNull List<AccessibilityShortcutInfo> installedAccessibilityShortcutList) {
        mInstalledAccessibilityShortcutList = installedAccessibilityShortcutList;
    }
}