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<>());