diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fbc96783597d1..d3ea52eb8888b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4892,4 +4892,7 @@
false
false
+
+
+
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a8d29d45ec0c4..b9f1e203552a4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4357,6 +4357,8 @@
+
+
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index fd355d8da341b..dc2628f0bc0bf 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
@@ -24,7 +26,9 @@ import android.app.AppOpsManager;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -33,11 +37,13 @@ import android.os.IBinder;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import libcore.util.EmptyArray;
@@ -666,13 +672,66 @@ public class AccessibilitySecurityPolicy {
/**
* Identifies whether the accessibility service is true and designed for accessibility. An
- * accessibility service is considered as accessibility category if
- * {@link AccessibilityServiceInfo#isAccessibilityTool} is true.
+ * accessibility service is considered as accessibility category if meets all conditions below:
+ *
+ * - {@link AccessibilityServiceInfo#isAccessibilityTool} is true
+ * - is installed from the trusted install source
+ *
*
* @param serviceInfo The accessibility service's serviceInfo.
* @return Returns true if it is a true accessibility service.
*/
public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) {
- return serviceInfo.isAccessibilityTool();
+ if (!serviceInfo.isAccessibilityTool()) {
+ return false;
+ }
+ if (!serviceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
+ return hasTrustedSystemInstallSource(
+ serviceInfo.getResolveInfo().serviceInfo.packageName);
+ }
+ return true;
+ }
+
+ /** Returns true if the {@code installedPackage} is installed from the trusted install source.
+ */
+ private boolean hasTrustedSystemInstallSource(String installedPackage) {
+ try {
+ InstallSourceInfo installSourceInfo = mPackageManager.getInstallSourceInfo(
+ installedPackage);
+ if (installSourceInfo == null) {
+ return false;
+ }
+ final String installSourcePackageName = installSourceInfo.getInitiatingPackageName();
+ if (installSourcePackageName == null || !mPackageManager.getPackageInfo(
+ installSourcePackageName,
+ 0).applicationInfo.isSystemApp()) {
+ return false;
+ }
+ return isTrustedInstallSource(installSourcePackageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(LOG_TAG, "can't find the package's install source:" + installedPackage);
+ }
+ return false;
+ }
+
+ /** Returns true if the {@code installerPackage} is a trusted install source. */
+ private boolean isTrustedInstallSource(String installerPackage) {
+ final String[] allowedInstallingSources = mContext.getResources().getStringArray(
+ com.android.internal.R.array
+ .config_accessibility_allowed_install_source);
+
+ if (allowedInstallingSources.length == 0) {
+ //Filters unwanted default installers if no allowed install sources.
+ String defaultInstaller = ArrayUtils.firstOrNull(LocalServices.getService(
+ PackageManagerInternal.class).getKnownPackageNames(PACKAGE_INSTALLER,
+ mCurrentUserId));
+ return !TextUtils.equals(defaultInstaller, installerPackage);
+ }
+ for (int i = 0; i < allowedInstallingSources.length; i++) {
+ if (TextUtils.equals(allowedInstallingSources[i], installerPackage)) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 45f43e8b672fe..ee00cb24a991e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -43,9 +43,12 @@ import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.SigningInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,8 +84,10 @@ public class AccessibilitySecurityPolicyTest {
private static final int APP_PID = 2000;
private static final int SYSTEM_PID = 558;
private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+ private static final String TEST_PACKAGE_NAME = "com.android.server.accessibility";
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
- "com.android.server.accessibility", "AccessibilitySecurityPolicyTest");
+ TEST_PACKAGE_NAME, "AccessibilitySecurityPolicyTest");
+ private static final String ALLOWED_INSTALL_PACKAGE_NAME = "com.allowed.install.package";
private static final int[] ALWAYS_DISPATCH_EVENTS = {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -142,19 +147,40 @@ public class AccessibilitySecurityPolicyTest {
@Mock
private AccessibilityServiceInfo mMockA11yServiceInfo;
@Mock
+ private ResolveInfo mMockResolveInfo;
+ @Mock
+ private ServiceInfo mMockServiceInfo;
+ @Mock
+ private ApplicationInfo mMockApplicationInfo;
+ @Mock
+ private ApplicationInfo mMockSourceApplicationInfo;
+ @Mock
+ private PackageInfo mMockSourcePackageInfo;
+ @Mock
private PolicyWarningUIController mPolicyWarningUIController;
@Before
- public void setUp() {
+ public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext.setMockPackageManager(mMockPackageManager);
mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager);
mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager);
mContext.getOrCreateTestableResources().addOverride(
R.dimen.accessibility_focus_highlight_stroke_width, 1);
+ mContext.getOrCreateTestableResources().addOverride(R.array
+ .config_accessibility_allowed_install_source,
+ new String[]{ALLOWED_INSTALL_PACKAGE_NAME});
+ when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
+ when(mMockPackageManager.getPackageInfo(ALLOWED_INSTALL_PACKAGE_NAME, 0)).thenReturn(
+ mMockSourcePackageInfo);
+
+ mMockResolveInfo.serviceInfo = mMockServiceInfo;
+ mMockServiceInfo.applicationInfo = mMockApplicationInfo;
+ mMockServiceInfo.packageName = TEST_PACKAGE_NAME;
+ mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
mPolicyWarningUIController, mContext, mMockA11yUserManager);
@@ -595,28 +621,7 @@ public class AccessibilitySecurityPolicyTest {
}
@Test
- public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() {
- final ArrayList boundServices = new ArrayList<>();
- boundServices.add(mMockA11yServiceConnection);
- when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
- }
-
- @Test
- public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() {
- onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction();
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
-
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
- any());
- }
-
- @Test
- public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() {
+ public void onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction() {
final ArrayList boundServices = new ArrayList<>();
boundServices.add(mMockA11yServiceConnection);
when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
@@ -628,8 +633,8 @@ public class AccessibilitySecurityPolicyTest {
}
@Test
- public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+ public void onBoundServicesChanged_unbindNonA11yToolService_activateUIControllerAction() {
+ onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
@@ -637,9 +642,66 @@ public class AccessibilitySecurityPolicyTest {
eq(TEST_COMPONENT_NAME));
}
+ @Test
+ public void onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction() {
+ final ArrayList boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockApplicationInfo.isSystemApp()).thenReturn(true);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_unbindSystemA11yToolService_noUIControllerAction() {
+ onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction();
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+ any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_bindAllowedSourceA11yToolService_noUIControllerAction()
+ throws PackageManager.NameNotFoundException {
+ final ArrayList boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockApplicationInfo.isSystemApp()).thenReturn(false);
+ final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
+ ALLOWED_INSTALL_PACKAGE_NAME, new SigningInfo(), null,
+ ALLOWED_INSTALL_PACKAGE_NAME);
+ when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+ installSourceInfo);
+ when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(true);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_bindUnknownSourceA11yToolService_activateUIControllerAction()
+ throws PackageManager.NameNotFoundException {
+ final ArrayList boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+ final InstallSourceInfo installSourceInfo = new InstallSourceInfo(null, null, null, null);
+ when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+ installSourceInfo);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
+ eq(TEST_COMPONENT_NAME));
+ }
+
@Test
public void onSwitchUser_differentUser_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+ onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());