Adds install source checks for accessibility service

Adds install source checks for accessibility services. An
accessibility category service must be installed from the
given allow list or not from the default installer if no
allow list provided.

Bug: 182959209
Test: atest AccessibilitySecurityPolicyTest

Change-Id: I023c15b9ebd87785e2f7eb1e218bec4ad006e52f
This commit is contained in:
lucychang
2021-04-22 13:54:43 +08:00
committed by Lucy Chang
parent 8215d0e109
commit f1f2c84756
4 changed files with 156 additions and 30 deletions

View File

@@ -4892,4 +4892,7 @@
<bool name="config_supportsMicToggle">false</bool>
<!-- Whether this device is supporting the camera toggle -->
<bool name="config_supportsCamToggle">false</bool>
<!-- List containing the allowed install sources for accessibility service. -->
<string-array name="config_accessibility_allowed_install_source" translatable="false"/>
</resources>

View File

@@ -4357,6 +4357,8 @@
<java-symbol type="drawable" name="ic_accessibility_24dp" />
<java-symbol type="string" name="view_and_control_notification_title" />
<java-symbol type="string" name="view_and_control_notification_content" />
<java-symbol type="array" name="config_accessibility_allowed_install_source" />
<!-- Translation -->
<java-symbol type="string" name="ui_translation_accessibility_translated_text" />
<java-symbol type="string" name="ui_translation_accessibility_translation_finished" />

View File

@@ -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:
* <ul>
* <li> {@link AccessibilityServiceInfo#isAccessibilityTool} is true</li>
* <li> is installed from the trusted install source</li>
* </ul>
*
* @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;
}
}

View File

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