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:
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<>());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user