Merge "Checks that prebundled categories contain only preinstalled contents." into main

This commit is contained in:
Daniel Norman
2024-07-23 19:14:59 +00:00
committed by Android (Google) Code Review
3 changed files with 87 additions and 40 deletions

View File

@@ -20,6 +20,16 @@ flag {
} }
} }
flag {
name: "check_prebundled_is_preinstalled"
namespace: "accessibility"
description: "Checks that all 'prebundled' components, used for grouping, are also preinstalled"
bug: "353888087"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag { flag {
name: "edit_shortcuts_in_full_screen" name: "edit_shortcuts_in_full_screen"
namespace: "accessibility" namespace: "accessibility"

View File

@@ -75,7 +75,8 @@ public class AccessibilitySettings extends DashboardFragment implements
private static final String CATEGORY_AUDIO = "audio_category"; private static final String CATEGORY_AUDIO = "audio_category";
private static final String CATEGORY_SPEECH = "speech_category"; private static final String CATEGORY_SPEECH = "speech_category";
private static final String CATEGORY_DISPLAY = "display_category"; private static final String CATEGORY_DISPLAY = "display_category";
private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category"; @VisibleForTesting
static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
private static final String CATEGORY_KEYBOARD_OPTIONS = "physical_keyboard_options_category"; private static final String CATEGORY_KEYBOARD_OPTIONS = "physical_keyboard_options_category";
@VisibleForTesting @VisibleForTesting
static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category"; static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
@@ -380,6 +381,7 @@ public class AccessibilitySettings extends DashboardFragment implements
} }
protected void updateServicePreferences() { protected void updateServicePreferences() {
final AccessibilityManager a11yManager = AccessibilityManager.getInstance(getPrefContext());
// Since services category is auto generated we have to do a pass // Since services category is auto generated we have to do a pass
// to generate it since services can come and go and then based on // to generate it since services can come and go and then based on
// the global accessibility state to decided whether it is enabled. // the global accessibility state to decided whether it is enabled.
@@ -410,8 +412,18 @@ public class AccessibilitySettings extends DashboardFragment implements
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM, AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL)); mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL));
final List<RestrictedPreference> preferenceList = getInstalledAccessibilityList( final List<AccessibilityShortcutInfo> installedShortcutList =
getPrefContext()); a11yManager.getInstalledAccessibilityShortcutListAsUser(getPrefContext(),
UserHandle.myUserId());
final List<AccessibilityServiceInfo> modifiableInstalledServiceList =
new ArrayList<>(a11yManager.getInstalledAccessibilityServiceList());
final List<RestrictedPreference> preferenceList = getInstalledAccessibilityPreferences(
getPrefContext(), installedShortcutList, modifiableInstalledServiceList);
if (Flags.checkPrebundledIsPreinstalled()) {
removeNonPreinstalledComponents(mPreBundledServiceComponentToCategoryMap,
installedShortcutList, modifiableInstalledServiceList);
}
final PreferenceCategory downloadedServicesCategory = final PreferenceCategory downloadedServicesCategory =
mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES); mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
@@ -456,13 +468,21 @@ public class AccessibilitySettings extends DashboardFragment implements
updatePreferenceCategoryVisibility(CATEGORY_KEYBOARD_OPTIONS); updatePreferenceCategoryVisibility(CATEGORY_KEYBOARD_OPTIONS);
} }
private List<RestrictedPreference> getInstalledAccessibilityList(Context context) { /**
final AccessibilityManager a11yManager = AccessibilityManager.getInstance(context); * Gets a list of {@link RestrictedPreference}s for the provided a11y shortcuts and services.
*
* <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.
*/
private List<RestrictedPreference> getInstalledAccessibilityPreferences(Context context,
List<AccessibilityShortcutInfo> installedShortcutList,
List<AccessibilityServiceInfo> modifiableInstalledServiceList) {
final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context); final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context);
final List<AccessibilityShortcutInfo> installedShortcutList =
a11yManager.getInstalledAccessibilityShortcutListAsUser(context,
UserHandle.myUserId());
final List<AccessibilityActivityPreference> activityList = final List<AccessibilityActivityPreference> activityList =
preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList); preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList);
final Set<Pair<String, CharSequence>> packageLabelPairs = final Set<Pair<String, CharSequence>> packageLabelPairs =
@@ -471,16 +491,14 @@ public class AccessibilitySettings extends DashboardFragment implements
a11yActivityPref.getPackageName(), a11yActivityPref.getLabel()) a11yActivityPref.getPackageName(), a11yActivityPref.getLabel())
).collect(Collectors.toSet()); ).collect(Collectors.toSet());
// Remove duplicate item here, new a ArrayList to copy unmodifiable list result // Remove duplicate A11yServices that are already shown as A11yActivities.
// (getInstalledAccessibilityServiceList).
final List<AccessibilityServiceInfo> installedServiceList = new ArrayList<>(
a11yManager.getInstalledAccessibilityServiceList());
if (!packageLabelPairs.isEmpty()) { if (!packageLabelPairs.isEmpty()) {
installedServiceList.removeIf( modifiableInstalledServiceList.removeIf(
target -> containsPackageAndLabelInList(packageLabelPairs, target)); target -> containsPackageAndLabelInList(packageLabelPairs, target));
} }
final List<RestrictedPreference> serviceList = final List<RestrictedPreference> serviceList =
preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList); preferenceHelper.createAccessibilityServicePreferenceList(
modifiableInstalledServiceList);
final List<RestrictedPreference> preferenceList = new ArrayList<>(); final List<RestrictedPreference> preferenceList = new ArrayList<>();
preferenceList.addAll(activityList); preferenceList.addAll(activityList);
@@ -489,6 +507,22 @@ public class AccessibilitySettings extends DashboardFragment implements
return preferenceList; return preferenceList;
} }
private static void removeNonPreinstalledComponents(
Map<ComponentName, PreferenceCategory> componentToCategory,
List<AccessibilityShortcutInfo> shortcutInfos,
List<AccessibilityServiceInfo> serviceInfos) {
for (AccessibilityShortcutInfo info : shortcutInfos) {
if (!info.getActivityInfo().applicationInfo.isSystemApp()) {
componentToCategory.remove(info.getComponentName());
}
}
for (AccessibilityServiceInfo info : serviceInfos) {
if (!info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
componentToCategory.remove(info.getComponentName());
}
}
}
private boolean containsPackageAndLabelInList( private boolean containsPackageAndLabelInList(
Set<Pair<String, CharSequence>> packageLabelPairs, Set<Pair<String, CharSequence>> packageLabelPairs,
AccessibilityServiceInfo targetServiceInfo) { AccessibilityServiceInfo targetServiceInfo) {

View File

@@ -26,11 +26,9 @@ import static org.robolectric.Shadows.shadowOf;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
@@ -110,9 +108,7 @@ public class AccessibilitySettingsTest {
private final Context mContext = ApplicationProvider.getApplicationContext(); private final Context mContext = ApplicationProvider.getApplicationContext();
@Spy @Spy
private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo( private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
PACKAGE_NAME, CLASS_NAME); new ComponentName(PACKAGE_NAME, CLASS_NAME));
@Mock
private AccessibilityShortcutInfo mShortcutInfo;
private ShadowAccessibilityManager mShadowAccessibilityManager; private ShadowAccessibilityManager mShadowAccessibilityManager;
@Mock @Mock
private LocalBluetoothManager mLocalBluetoothManager; private LocalBluetoothManager mLocalBluetoothManager;
@@ -125,7 +121,6 @@ public class AccessibilitySettingsTest {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>()); mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat); mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
setMockAccessibilityShortcutInfo(mShortcutInfo);
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
@@ -398,14 +393,25 @@ public class AccessibilitySettingsTest {
public void testAccessibilityMenuInSystem_IncludedInInteractionControl() { public void testAccessibilityMenuInSystem_IncludedInInteractionControl() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList( mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(getMockAccessibilityServiceInfo( List.of(getMockAccessibilityServiceInfo(
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))); AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
/*isSystemApp=*/true)));
setupFragment(); setupFragment();
final RestrictedPreference pref = mFragment.getPreferenceScreen().findPreference( assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM.flattenToString()); .isEqualTo(AccessibilitySettings.CATEGORY_INTERACTION_CONTROL);
final String prefCategory = mFragment.mServicePreferenceToPreferenceCategoryMap.get( }
pref).getKey();
assertThat(prefCategory).isEqualTo(AccessibilitySettings.CATEGORY_INTERACTION_CONTROL); @Test
@EnableFlags(com.android.settings.accessibility.Flags.FLAG_CHECK_PREBUNDLED_IS_PREINSTALLED)
public void testNonPreinstalledApp_IncludedInDownloadedCategory() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(getMockAccessibilityServiceInfo(
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
/*isSystemApp=*/false)));
setupFragment();
assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
.isEqualTo(AccessibilitySettings.CATEGORY_DOWNLOADED_SERVICES);
} }
@Test @Test
@@ -418,13 +424,20 @@ public class AccessibilitySettingsTest {
assertThat(pref).isNull(); assertThat(pref).isNull();
} }
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName, private String getPreferenceCategory(ComponentName componentName) {
String className) { return mFragment.mServicePreferenceToPreferenceCategoryMap.get(
return getMockAccessibilityServiceInfo(new ComponentName(packageName, className)); mFragment.getPreferenceScreen().findPreference(
componentName.flattenToString())).getKey();
} }
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) { private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) {
final ApplicationInfo applicationInfo = new ApplicationInfo(); return getMockAccessibilityServiceInfo(componentName, true);
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName,
boolean isSystemApp) {
final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
when(applicationInfo.isSystemApp()).thenReturn(isSystemApp);
final ServiceInfo serviceInfo = new ServiceInfo(); final ServiceInfo serviceInfo = new ServiceInfo();
applicationInfo.packageName = componentName.getPackageName(); applicationInfo.packageName = componentName.getPackageName();
serviceInfo.packageName = componentName.getPackageName(); serviceInfo.packageName = componentName.getPackageName();
@@ -445,16 +458,6 @@ public class AccessibilitySettingsTest {
return null; return null;
} }
private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) {
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);
}
private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) { private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {
info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;