diff options
7 files changed, 116 insertions, 18 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 363c37b9e203..a23277eddf2b 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 fe5d4533a598..20850af97429 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -603,7 +603,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, |