From 9e78be15cf19de3ab8697a7945aa3e96d0d36b2f Mon Sep 17 00:00:00 2001 From: Sally Date: Mon, 27 Feb 2023 22:39:40 +0000 Subject: [PATCH] Require CREATE_VIRTUAL_DEVICE permission to register or unregister an AccessibilityDisplayProxy This permission is only available to CDM apps with certain internal roles. When associating with a companion device, a dialog asks users to approve access, and on success this grants the CREATE_VIRTUAL_DEVICE permission. To prevent abuse of accessibility APIs, accessibility will leverage this existing flow for callers who want to register or unregister a proxy. Bug: 265858731 Test: atest AccessibilityDisplayProxyTest, manually test the association flow Change-Id: Ia2091b4a0f358cf4fe4aa624f69ec633523e1b36 --- core/api/system-current.txt | 4 +-- .../accessibility/AccessibilityManager.java | 12 ++++++--- .../AccessibilityManagerService.java | 2 ++ .../AccessibilityManagerServiceTest.java | 27 +++++++++++++++++-- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 77fb8d09ed971..3432ba4307789 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -17283,9 +17283,9 @@ package android.view.accessibility { public final class AccessibilityManager { method public int getAccessibilityWindowId(@Nullable android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut(); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); + method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); + method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index aa631cfa59804..9504852f6e98b 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -2068,12 +2068,14 @@ public final class AccessibilityManager { * {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed. * * @throws SecurityException if the app does not hold the - * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission. + * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the + * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, + Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { @@ -2096,12 +2098,14 @@ public final class AccessibilityManager { * @return {@code true} if the proxy is successfully unregistered. * * @throws SecurityException if the app does not hold the - * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission. + * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the + * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, + Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 25381be88adde..7fba72b74e8d6 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3801,6 +3801,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public boolean registerProxyForDisplay(IAccessibilityServiceClient client, int displayId) throws RemoteException { mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); if (client == null) { return false; } @@ -3837,6 +3838,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public boolean unregisterProxyForDisplay(int displayId) { mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); final long identity = Binder.clearCallingIdentity(); try { return mProxyManager.unregisterProxy(displayId); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 503e579062d44..96eca7171af09 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -289,7 +289,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testRegisterProxyWithoutPermission() throws Exception { + public void testRegisterProxyWithoutA11yPermission() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy) .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); @@ -299,6 +299,18 @@ public class AccessibilityManagerServiceTest { any(), any(), any()); } + @SmallTest + @Test + public void testRegisterProxyWithoutDevicePermission() throws Exception { + doThrow(SecurityException.class).when(mMockSecurityPolicy) + .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + + assertThrows(SecurityException.class, + () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY)); + verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(), + any(), any(), any()); + } + @SmallTest @Test public void testRegisterProxyForDefaultDisplay() throws Exception { @@ -328,7 +340,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testUnRegisterProxyWithoutPermission() throws Exception { + public void testUnRegisterProxyWithoutA11yPermission() { doThrow(SecurityException.class).when(mMockSecurityPolicy) .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); @@ -337,6 +349,17 @@ public class AccessibilityManagerServiceTest { verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY); } + @SmallTest + @Test + public void testUnRegisterProxyWithoutDevicePermission() { + doThrow(SecurityException.class).when(mMockSecurityPolicy) + .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + + assertThrows(SecurityException.class, + () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY)); + verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY); + } + @SmallTest @Test public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {