summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Bernardo Rufino <brufino@google.com> 2020-04-01 15:19:49 +0100
committer Bernardo Rufino <brufino@google.com> 2020-04-22 17:23:52 +0000
commitc7988a90c749e5c2f8a19d50661492a39c3ec394 (patch)
tree5be80254405fdb18aa011747623e28e1047059b5
parent6d5625c5cee23435cfa55ff16fcb57f33caafdae (diff)
Allow sysUI to send a11y events for other package and user
As discussed, we: * Allow apps with INTERACT_ACCROSS_USERS(_FULL) to specify an explicit user (instead of just USER_CURRENT). * Introduce new signature permission ACT_AS_PACKAGE_FOR_ACCESSIBILITY and grant it to sysUI. This permissions allow holders to specify another package on behalf of which they can perform a11y operations. This is for toasts since now sysUI renders toasts on behalf of the app for apps targeting R+. Bug: 152839254 Test: atest FrameworksServicesTests:AccessibilitySecurityPolicyTest FrameworksServicesTests:AccessibilityWindowManagerTest android.widget.cts.ToastTest Change-Id: I3541045d574518571f348051d53e24ff1a4a67ef
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java109
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java2
7 files changed, 116 insertions, 18 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ee25ac27b25c..62c2c5124ae2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3201,6 +3201,11 @@
<permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to perform accessibility operations (e.g. send events) on
+ behalf of another package. -->
+ <permission android:name="android.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to change the accessibility volume. -->
<permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME"
android:protectionLevel="signature" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 133d375b8c6e..37900fb13496 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -219,6 +219,7 @@
<!-- accessibility -->
<uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" />
<uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
+ <uses-permission android:name="android.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY" />
<!-- to control accessibility volume -->
<uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1a72cf023453..c7eb3ce37f4f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -599,7 +599,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// Make sure the reported package is one the caller has access to.
event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked(
- event.getPackageName(), UserHandle.getCallingAppId(), resolvedUserId));
+ event.getPackageName(), UserHandle.getCallingAppId(), resolvedUserId,
+ getCallingPid()));
// This method does nothing for a background user.
if (resolvedUserId == mCurrentUserId) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index d98e31eadb22..41f32075fb77 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -167,11 +167,12 @@ public class AccessibilitySecurityPolicy {
* @param packageName The package name the app wants to expose
* @param appId The app's id
* @param userId The app's user id
+ * @param pid The app's process pid that requested this
* @return A package name that is valid to report
*/
@Nullable
public String resolveValidReportedPackageLocked(
- @Nullable CharSequence packageName, int appId, int userId) {
+ @Nullable CharSequence packageName, int appId, int userId, int pid) {
// Okay to pass no package
if (packageName == null) {
return null;
@@ -191,6 +192,11 @@ public class AccessibilitySecurityPolicy {
.getHostedWidgetPackages(resolvedUid), packageNameStr)) {
return packageName.toString();
}
+ // If app has the targeted permission to act as another package
+ if (mContext.checkPermission(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY,
+ pid, resolvedUid) == PackageManager.PERMISSION_GRANTED) {
+ return packageName.toString();
+ }
// Otherwise, set the package to the first one in the UID
final String[] packageNames = mPackageManager.getPackagesForUid(resolvedUid);
if (ArrayUtils.isEmpty(packageNames)) {
@@ -403,8 +409,7 @@ public class AccessibilitySecurityPolicy {
|| userId == UserHandle.USER_CURRENT_OR_SELF) {
return currentUserId;
}
- throw new IllegalArgumentException("Calling user can be changed to only "
- + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
+ return resolveProfileParentLocked(userId);
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 5d97d213928f..468e93a8f683 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -955,7 +955,8 @@ public class AccessibilityWindowManager {
// Makes sure the reported package is one the caller has access to.
packageName = mSecurityPolicy.resolveValidReportedPackageLocked(
- packageName, UserHandle.getCallingAppId(), resolvedUserId);
+ packageName, UserHandle.getCallingAppId(), resolvedUserId,
+ Binder.getCallingPid());
windowId = sNextWindowId++;
// If the window is from a process that runs across users such as
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 5a96347c4ae1..cc8ac86d6b59 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -28,10 +28,12 @@ import static org.junit.Assert.assertThat;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.AppOpsManager;
import android.appwidget.AppWidgetManagerInternal;
@@ -71,6 +73,8 @@ public class AccessibilitySecurityPolicyTest {
private static final int WINDOWID = 0x000a;
private static final int WINDOWID2 = 0x000b;
private static final int APP_UID = 10400;
+ private static final int APP_PID = 2000;
+ private static final int SYSTEM_PID = 558;
private static final String PERMISSION = "test-permission";
private static final String FUNCTION = "test-function-name";
@@ -196,13 +200,13 @@ public class AccessibilitySecurityPolicyTest {
@Test
public void resolveValidReportedPackage_nullPkgName_returnNull() {
assertNull(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- null, Process.SYSTEM_UID, UserHandle.USER_SYSTEM));
+ null, Process.SYSTEM_UID, UserHandle.USER_SYSTEM, SYSTEM_PID));
}
@Test
public void resolveValidReportedPackage_uidIsSystem_returnPkgName() {
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- PACKAGE_NAME, Process.SYSTEM_UID, UserHandle.USER_SYSTEM),
+ PACKAGE_NAME, Process.SYSTEM_UID, UserHandle.USER_SYSTEM, SYSTEM_PID),
PACKAGE_NAME);
}
@@ -213,7 +217,7 @@ public class AccessibilitySecurityPolicyTest {
.thenReturn(APP_UID);
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- PACKAGE_NAME, APP_UID, UserHandle.USER_SYSTEM),
+ PACKAGE_NAME, APP_UID, UserHandle.USER_SYSTEM, APP_PID),
PACKAGE_NAME);
}
@@ -221,6 +225,7 @@ public class AccessibilitySecurityPolicyTest {
public void resolveValidReportedPackage_uidIsWidgetHost_pkgNameIsAppWidget_returnPkgName()
throws PackageManager.NameNotFoundException {
final int widgetHostUid = APP_UID;
+ final int widgetHostPid = APP_PID;
final String hostPackageName = PACKAGE_NAME;
final String widgetPackageName = PACKAGE_NAME2;
final ArraySet<String> widgetPackages = new ArraySet<>();
@@ -232,7 +237,7 @@ public class AccessibilitySecurityPolicyTest {
.thenReturn(widgetHostUid);
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- widgetPackageName, widgetHostUid, UserHandle.USER_SYSTEM),
+ widgetPackageName, widgetHostUid, UserHandle.USER_SYSTEM, widgetHostPid),
widgetPackageName);
}
@@ -247,10 +252,52 @@ public class AccessibilitySecurityPolicyTest {
.thenThrow(PackageManager.NameNotFoundException.class);
when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
.thenReturn(new ArraySet<>());
+ when(mMockContext.checkPermission(
+ eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
- assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- invalidPackageName, APP_UID, UserHandle.USER_SYSTEM),
- PACKAGE_NAME);
+ assertEquals(PACKAGE_NAME, mA11ySecurityPolicy.resolveValidReportedPackageLocked(
+ invalidPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID));
+ }
+
+ @Test
+ public void resolveValidReportedPackage_anotherPkgNameWithActAsPkgPermission_returnPkg()
+ throws PackageManager.NameNotFoundException {
+ final String wantedPackageName = PACKAGE_NAME2;
+ final int wantedUid = APP_UID + 1;
+ final String[] uidPackages = {PACKAGE_NAME};
+ when(mMockPackageManager.getPackagesForUid(APP_UID))
+ .thenReturn(uidPackages);
+ when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, UserHandle.USER_SYSTEM))
+ .thenReturn(wantedUid);
+ when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
+ .thenReturn(new ArraySet<>());
+ when(mMockContext.checkPermission(
+ eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID)))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ assertEquals(wantedPackageName, mA11ySecurityPolicy.resolveValidReportedPackageLocked(
+ wantedPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID));
+ }
+
+ @Test
+ public void resolveValidReportedPackage_anotherPkgNameWithoutActAsPkgPermission_returnUidPkg()
+ throws PackageManager.NameNotFoundException {
+ final String wantedPackageName = PACKAGE_NAME2;
+ final int wantedUid = APP_UID + 1;
+ final String[] uidPackages = {PACKAGE_NAME};
+ when(mMockPackageManager.getPackagesForUid(APP_UID))
+ .thenReturn(uidPackages);
+ when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, UserHandle.USER_SYSTEM))
+ .thenReturn(wantedUid);
+ when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
+ .thenReturn(new ArraySet<>());
+ when(mMockContext.checkPermission(
+ eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+
+ assertEquals(PACKAGE_NAME, mA11ySecurityPolicy.resolveValidReportedPackageLocked(
+ wantedPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID));
}
@Test
@@ -432,21 +479,59 @@ public class AccessibilitySecurityPolicyTest {
UserHandle.USER_CURRENT_OR_SELF);
}
- @Test(expected = IllegalArgumentException.class)
- public void resolveCallingUserId_callingParentNotCurrentUser_userIdIsInvalid_shouldException() {
+ @Test
+ public void resolveCallingUserId_anotherUserIdWithCrossUserPermission_returnUserId() {
final AccessibilitySecurityPolicy spySecurityPolicy = Mockito.spy(mA11ySecurityPolicy);
final int callingUserId = UserHandle.getUserId(Process.myUid());
final int callingParentId = 20;
final int currentUserId = 30;
- final int invalidUserId = 40;
+ final int wantedUserId = 40;
when(mMockA11yUserManager.getCurrentUserIdLocked())
.thenReturn(currentUserId);
doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked(
callingUserId);
- when(mMockContext.checkCallingPermission(any()))
+ when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ assertEquals(wantedUserId,
+ spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId));
+ }
+
+ @Test
+ public void resolveCallingUserId_anotherUserIdWithCrossUserFullPermission_returnUserId() {
+ final AccessibilitySecurityPolicy spySecurityPolicy = Mockito.spy(mA11ySecurityPolicy);
+ final int callingUserId = UserHandle.getUserId(Process.myUid());
+ final int callingParentId = 20;
+ final int currentUserId = 30;
+ final int wantedUserId = 40;
+ when(mMockA11yUserManager.getCurrentUserIdLocked())
+ .thenReturn(currentUserId);
+ doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked(
+ callingUserId);
+ when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(invalidUserId);
+ assertEquals(wantedUserId,
+ spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId));
+ }
+
+ @Test(expected = SecurityException.class)
+ public void resolveCallingUserId_anotherUserIdWithoutCrossUserPermission_shouldException() {
+ final AccessibilitySecurityPolicy spySecurityPolicy = Mockito.spy(mA11ySecurityPolicy);
+ final int callingUserId = UserHandle.getUserId(Process.myUid());
+ final int callingParentId = 20;
+ final int currentUserId = 30;
+ final int wantedUserId = 40;
+ when(mMockA11yUserManager.getCurrentUserIdLocked())
+ .thenReturn(currentUserId);
+ doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked(
+ callingUserId);
+ when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+
+ spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 10a86f9ea527..e4d51e4374a7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -134,7 +134,7 @@ public class AccessibilityWindowManagerTest {
when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID);
when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked(
- anyString(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
+ anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
mA11yWindowManager = new AccessibilityWindowManager(new Object(), mHandler,
mMockWindowManagerInternal,