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:
Daniel Norman
2024-08-14 21:02:27 +00:00
parent 9148e612fe
commit c38fd822ba
3 changed files with 89 additions and 44 deletions

View File

@@ -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;