Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7431cce6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "display_proxy_permission" into main

* changes:
  Improve permissions for display proxy methods
  Simplify ProxyManager#displayBelongsToCaller
parents ae11565a 661e6a10
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -147,12 +147,12 @@ interface IAccessibilityManager {
    @RequiresNoPermission
    oneway void setAccessibilityWindowAttributes(int displayId, int windowId, int userId, in AccessibilityWindowAttributes attributes);

    // Requires CREATE_VIRTUAL_DEVICE permission. Also requires either a11y permission or role.
    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
    // Requires the caller to either hold the MANAGE_ACCESSIBILITY permission or own the provided
    // displayId via VirtualDeviceManager.
    boolean registerProxyForDisplay(IAccessibilityServiceClient proxy, int displayId);

    // Requires CREATE_VIRTUAL_DEVICE permission. Also requires either a11y permission or role.
    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
    // Requires the caller to either hold the MANAGE_ACCESSIBILITY permission or own the provided
    // displayId via VirtualDeviceManager.
    boolean unregisterProxyForDisplay(int displayId);

    // Used by UiAutomation for tests on the InputFilter
+2 −11
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.server.accessibility;

import static android.Manifest.permission.CREATE_VIRTUAL_DEVICE;
import static android.Manifest.permission.INJECT_EVENTS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACCESSIBILITY;
@@ -5032,11 +5031,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
    }

    @Override
    @EnforcePermission(CREATE_VIRTUAL_DEVICE)
    public boolean registerProxyForDisplay(IAccessibilityServiceClient client, int displayId)
            throws RemoteException {
        registerProxyForDisplay_enforcePermission();
        mSecurityPolicy.checkForAccessibilityPermissionOrRole();
        mSecurityPolicy.checkForAccessibilityPermissionOrDisplayOwnership(displayId);
        enforceCurrentUserIfVisibleBackgroundEnabled();
        if (client == null) {
            return false;
@@ -5052,10 +5049,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
            throw new IllegalArgumentException("The display " + displayId + " is already being"
                    + " proxy-ed");
        }
        if (!mProxyManager.displayBelongsToCaller(Binder.getCallingUid(), displayId)) {
            throw new SecurityException("The display " + displayId + " does not belong to"
                    + " the caller.");
        }

        final long identity = Binder.clearCallingIdentity();
        try {
@@ -5072,10 +5065,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
    }

    @Override
    @EnforcePermission(CREATE_VIRTUAL_DEVICE)
    public boolean unregisterProxyForDisplay(int displayId) {
        unregisterProxyForDisplay_enforcePermission();
        mSecurityPolicy.checkForAccessibilityPermissionOrRole();
        mSecurityPolicy.checkForAccessibilityPermissionOrDisplayOwnership(displayId);
        final long identity = Binder.clearCallingIdentity();
        try {
            return mProxyManager.unregisterProxy(displayId);
+18 −26
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.server.accessibility;

import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_BY_ADMIN;
import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;

import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -26,7 +25,6 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.role.RoleManager;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -47,6 +45,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.InputMethodInfo;

import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;

import libcore.util.EmptyArray;
@@ -675,39 +675,31 @@ public class AccessibilitySecurityPolicy {
        }
    }

    /** Returns true if the display belongs to one of the caller's virtual devices. */
    private boolean displayBelongsToCaller(int callingUid, int proxyDisplayId) {
        final VirtualDeviceManagerInternal localVdm = LocalServices.getService(
                VirtualDeviceManagerInternal.class);
        if (localVdm == null) {
            return false;
        }
        int deviceId = localVdm.getDeviceIdForDisplayId(proxyDisplayId);
        return callingUid == localVdm.getDeviceOwnerUid(deviceId);
    }

    /**
     * Throws a SecurityException if the caller has neither the MANAGE_ACCESSIBILITY permission nor
     * the COMPANION_DEVICE_APP_STREAMING role.
     * owns the provided displayId in any VirtualDevice.
     */
    public void checkForAccessibilityPermissionOrRole() {
    public void checkForAccessibilityPermissionOrDisplayOwnership(int displayId) {
        final boolean canManageAccessibility =
                mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
                        == PackageManager.PERMISSION_GRANTED;
        if (canManageAccessibility) {
            return;
        }
        final int callingUid = Binder.getCallingUid();
        final long identity = Binder.clearCallingIdentity();
        try {
            final RoleManager roleManager = mContext.getSystemService(RoleManager.class);
            if (roleManager != null) {
                final List<String> holders = roleManager.getRoleHoldersAsUser(
                        DEVICE_PROFILE_APP_STREAMING, UserHandle.getUserHandleForUid(callingUid));
                final String[] packageNames = mPackageManager.getPackagesForUid(callingUid);
                if (packageNames != null) {
                    for (String packageName : packageNames) {
                        if (holders.contains(packageName)) {
                            return;
                        }
                    }
                }
            }
            throw new SecurityException(
                    "Cannot register a proxy for a device without the "
                            + "android.app.role.COMPANION_DEVICE_APP_STREAMING role or the"
                            + " MANAGE_ACCESSIBILITY permission.");
        } finally {
            Binder.restoreCallingIdentity(identity);
        if (!displayBelongsToCaller(Binder.getCallingUid(), displayId)) {
            throw new SecurityException("The display " + displayId + " does not belong to"
                    + " the caller.");
        }
    }

+1 −21
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName;
import android.content.Context;
@@ -376,25 +375,6 @@ public class ProxyManager {
        return isTrackingDeviceId;
    }

    /** Returns true if the display belongs to one of the caller's virtual devices. */
    public boolean displayBelongsToCaller(int callingUid, int proxyDisplayId) {
        final VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
        final VirtualDeviceManagerInternal localVdm = getLocalVdm();
        if (vdm == null || localVdm == null) {
            return false;
        }
        final List<VirtualDevice> virtualDevices = vdm.getVirtualDevices();
        for (VirtualDevice device : virtualDevices) {
            if (localVdm.getDisplayIdsForDevice(device.getDeviceId()).contains(proxyDisplayId)) {
                final int ownerUid = localVdm.getDeviceOwnerUid(device.getDeviceId());
                if (callingUid == ownerUid) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Sends AccessibilityEvents to a proxy given the event's displayId.
     */
+5 −28
Original line number Diff line number Diff line
@@ -434,8 +434,6 @@ public class AccessibilityManagerServiceTest {
    @SmallTest
    @Test
    public void testRegisterProxy() throws Exception {
        mFakePermissionEnforcer.grant(Manifest.permission.CREATE_VIRTUAL_DEVICE);
        when(mProxyManager.displayBelongsToCaller(anyInt(), anyInt())).thenReturn(true);
        mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
        verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY), anyInt(),
                eq(mMockSecurityPolicy),
@@ -446,9 +444,8 @@ public class AccessibilityManagerServiceTest {
    @SmallTest
    @Test
    public void testRegisterProxyWithoutA11yPermissionOrRole() throws Exception {
        mFakePermissionEnforcer.grant(Manifest.permission.CREATE_VIRTUAL_DEVICE);
        doThrow(SecurityException.class).when(mMockSecurityPolicy)
                .checkForAccessibilityPermissionOrRole();
                .checkForAccessibilityPermissionOrDisplayOwnership(TEST_DISPLAY);

        assertThrows(SecurityException.class,
                () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
@@ -456,19 +453,11 @@ public class AccessibilityManagerServiceTest {
                any(), any(), any());
    }

    @SmallTest
    @Test
    public void testRegisterProxyWithoutDevicePermission() throws Exception {
        mFakePermissionEnforcer.revoke(Manifest.permission.CREATE_VIRTUAL_DEVICE);
        assertThrows(SecurityException.class,
                () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
        verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
                any(), any(), any());
    }

    @SmallTest
    @Test
    public void testRegisterProxyForDefaultDisplay() throws Exception {
        doThrow(SecurityException.class).when(mMockSecurityPolicy)
                .checkForAccessibilityPermissionOrDisplayOwnership(Display.DEFAULT_DISPLAY);
        assertThrows(SecurityException.class,
                () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
        verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
@@ -478,7 +467,6 @@ public class AccessibilityManagerServiceTest {
    @SmallTest
    @Test
    public void testRegisterProxyForInvalidDisplay() throws Exception {
        mFakePermissionEnforcer.grant(Manifest.permission.CREATE_VIRTUAL_DEVICE);
        assertThrows(IllegalArgumentException.class,
                () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY));
        verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
@@ -488,8 +476,6 @@ public class AccessibilityManagerServiceTest {
    @SmallTest
    @Test
    public void testUnRegisterProxyWithPermission() throws Exception {
        mFakePermissionEnforcer.grant(Manifest.permission.CREATE_VIRTUAL_DEVICE);
        when(mProxyManager.displayBelongsToCaller(anyInt(), anyInt())).thenReturn(true);
        mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
        mA11yms.unregisterProxyForDisplay(TEST_DISPLAY);

@@ -498,19 +484,10 @@ public class AccessibilityManagerServiceTest {

    @SmallTest
    @Test
    public void testUnRegisterProxyWithoutA11yPermissionOrRole() {
    public void testUnRegisterProxyWithoutA11yPermissionOrDisplayOwnership() {
        doThrow(SecurityException.class).when(mMockSecurityPolicy)
                .checkForAccessibilityPermissionOrRole();

        assertThrows(SecurityException.class,
                () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY));
        verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY);
    }
                .checkForAccessibilityPermissionOrDisplayOwnership(TEST_DISPLAY);

    @SmallTest
    @Test
    public void testUnRegisterProxyWithoutDevicePermission() {
        mFakePermissionEnforcer.revoke(Manifest.permission.CREATE_VIRTUAL_DEVICE);
        assertThrows(SecurityException.class,
                () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY));
        verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY);