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 Change-Id: Ie680e80169aa734f2559fe50ef06e4d1eae46779
This commit is contained in:
@@ -23,7 +23,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;
|
||||
@@ -31,7 +30,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;
|
||||
|
||||
@@ -59,8 +57,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)
|
||||
@@ -415,14 +411,14 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
final List<AccessibilityShortcutInfo> installedShortcutList =
|
||||
a11yManager.getInstalledAccessibilityShortcutListAsUser(getPrefContext(),
|
||||
UserHandle.myUserId());
|
||||
final List<AccessibilityServiceInfo> modifiableInstalledServiceList =
|
||||
new ArrayList<>(a11yManager.getInstalledAccessibilityServiceList());
|
||||
final List<AccessibilityServiceInfo> installedServiceList =
|
||||
a11yManager.getInstalledAccessibilityServiceList();
|
||||
final List<RestrictedPreference> preferenceList = getInstalledAccessibilityPreferences(
|
||||
getPrefContext(), installedShortcutList, modifiableInstalledServiceList);
|
||||
getPrefContext(), installedShortcutList, installedServiceList);
|
||||
|
||||
if (Flags.checkPrebundledIsPreinstalled()) {
|
||||
removeNonPreinstalledComponents(mPreBundledServiceComponentToCategoryMap,
|
||||
installedShortcutList, modifiableInstalledServiceList);
|
||||
installedShortcutList, installedServiceList);
|
||||
}
|
||||
|
||||
final PreferenceCategory downloadedServicesCategory =
|
||||
@@ -474,31 +470,18 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
* <p>{@code modifiableInstalledServiceList} may be modified to remove any entries with
|
||||
* matching package name and label as an entry in {@code installedShortcutList}.
|
||||
*
|
||||
* @param installedShortcutList A list of installed {@link AccessibilityShortcutInfo}s.
|
||||
* @param modifiableInstalledServiceList A modifiable list of installed
|
||||
* {@link AccessibilityServiceInfo}s.
|
||||
* @param installedShortcutList A list of installed {@link AccessibilityShortcutInfo}s.
|
||||
* @param installedServiceList A list of installed {@link AccessibilityServiceInfo}s.
|
||||
*/
|
||||
private List<RestrictedPreference> getInstalledAccessibilityPreferences(Context context,
|
||||
List<AccessibilityShortcutInfo> installedShortcutList,
|
||||
List<AccessibilityServiceInfo> modifiableInstalledServiceList) {
|
||||
List<AccessibilityServiceInfo> installedServiceList) {
|
||||
final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context);
|
||||
|
||||
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 A11yServices that are already shown as A11yActivities.
|
||||
if (!packageLabelPairs.isEmpty()) {
|
||||
modifiableInstalledServiceList.removeIf(
|
||||
target -> containsPackageAndLabelInList(packageLabelPairs, target));
|
||||
}
|
||||
final List<RestrictedPreference> serviceList =
|
||||
preferenceHelper.createAccessibilityServicePreferenceList(
|
||||
modifiableInstalledServiceList);
|
||||
preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList);
|
||||
|
||||
final List<RestrictedPreference> preferenceList = new ArrayList<>();
|
||||
preferenceList.addAll(activityList);
|
||||
@@ -523,16 +506,6 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@@ -26,10 +26,13 @@ import static org.robolectric.Shadows.shadowOf;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.accessibilityservice.AccessibilityShortcutInfo;
|
||||
import android.content.ComponentName;
|
||||
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;
|
||||
@@ -48,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;
|
||||
@@ -73,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;
|
||||
|
||||
@@ -85,6 +88,7 @@ import java.util.List;
|
||||
/** Test for {@link AccessibilitySettings}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
ShadowAccessibilityManager.class,
|
||||
ShadowBluetoothAdapter.class,
|
||||
ShadowUserManager.class,
|
||||
ShadowColorDisplayManager.class,
|
||||
@@ -93,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";
|
||||
@@ -108,7 +114,7 @@ public class AccessibilitySettingsTest {
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
@Spy
|
||||
private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
|
||||
new ComponentName(PACKAGE_NAME, CLASS_NAME));
|
||||
SERVICE_COMPONENT_NAME);
|
||||
private ShadowAccessibilityManager mShadowAccessibilityManager;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@@ -117,7 +123,8 @@ 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;
|
||||
@@ -369,7 +376,7 @@ public class AccessibilitySettingsTest {
|
||||
mFragment.onContentChanged();
|
||||
|
||||
RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
|
||||
COMPONENT_NAME.flattenToString());
|
||||
SERVICE_COMPONENT_NAME.flattenToString());
|
||||
|
||||
assertThat(preference).isNotNull();
|
||||
|
||||
@@ -389,7 +396,7 @@ public class AccessibilitySettingsTest {
|
||||
mFragment.onResume();
|
||||
|
||||
RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
|
||||
COMPONENT_NAME.flattenToString());
|
||||
SERVICE_COMPONENT_NAME.flattenToString());
|
||||
|
||||
assertThat(preference).isNotNull();
|
||||
|
||||
@@ -430,6 +437,36 @@ public class AccessibilitySettingsTest {
|
||||
assertThat(pref).isNull();
|
||||
}
|
||||
|
||||
@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 String getPreferenceCategory(ComponentName componentName) {
|
||||
return mFragment.mServicePreferenceToPreferenceCategoryMap.get(
|
||||
mFragment.getPreferenceScreen().findPreference(
|
||||
@@ -444,11 +481,12 @@ public class AccessibilitySettingsTest {
|
||||
boolean isSystemApp) {
|
||||
final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
|
||||
when(applicationInfo.isSystemApp()).thenReturn(isSystemApp);
|
||||
final ServiceInfo serviceInfo = new ServiceInfo();
|
||||
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;
|
||||
@@ -464,6 +502,18 @@ public class AccessibilitySettingsTest {
|
||||
return null;
|
||||
}
|
||||
|
||||
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(ACTIVITY_COMPONENT_NAME);
|
||||
return mockInfo;
|
||||
}
|
||||
|
||||
private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {
|
||||
info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
|
||||
info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user