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

Commit 24c03513 authored by Vladimir Komsiyski's avatar Vladimir Komsiyski
Browse files

VDM limitations for untrusted displays

 - no input support
 - no wakelock
 - put them in the default display group
 - no pointer icon access
 - no custom IME
 - always show apps running on these displays in recents
 - enforce ADD_ALWAYS_UNLOCKED_DISPLAY permission to set the
   virtual device lock state to LOCK_STATE_ALWAYS_UNLOCKED
 - enforce ADD_TRUSTED_DISPLAY permission to set the
   virtual device clipboard policy to CUSTOM
 - ensure that all displays for a device with a CUSTOM
   clipboard policy are trusted

Fix: 370653882
Fix: 370656714
Fix: 370653884
Fix: 371132007
Fix: 370653363
Fix: 370657091
Fix: 370653365
Test: presubmit
Flag: EXEMPT bugfix
Change-Id: If794df68aa261e9656c92e3cfc4aaf02b82a3ea7
parent 4c00223a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1135,8 +1135,11 @@ public final class VirtualDeviceManager {
        /**
         * Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
         *
         * <p>Only applicable to trusted displays.</p>
         *
         * @param showPointerIcon True if the pointer should be shown; false otherwise. The default
         *   visibility is true.
         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
         */
        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
        public void setShowPointerIcon(boolean showPointerIcon) {
+14 −6
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ public final class VirtualDeviceParams implements Parcelable {
     * @hide
     */
    @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS, POLICY_TYPE_AUDIO,
            POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CAMERA,
            POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CLIPBOARD, POLICY_TYPE_CAMERA,
            POLICY_TYPE_BLOCKED_ACTIVITY})
    @Retention(RetentionPolicy.SOURCE)
    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@@ -220,11 +220,16 @@ public final class VirtualDeviceParams implements Parcelable {
     * Tells the activity manager how to handle recents entries for activities run on this device.
     *
     * <ul>
     *     <li>{@link #DEVICE_POLICY_DEFAULT}: Activities launched on VirtualDisplays owned by this
     *     <li>{@link #DEVICE_POLICY_DEFAULT}: Activities launched on trusted displays owned by this
     *     device will appear in the host device recents.
     *     <li>{@link #DEVICE_POLICY_CUSTOM}: Activities launched on VirtualDisplays owned by this
     *     <li>{@link #DEVICE_POLICY_CUSTOM}: Activities launched on trusted displays owned by this
     *     device will not appear in recents.
     * </ul>
     *
     * <p>Activities launched on untrusted displays will always show in the host device recents,
     * regardless of the policy.</p>
     *
     * @see android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED
     */
    public static final int POLICY_TYPE_RECENTS = 2;

@@ -254,8 +259,10 @@ public final class VirtualDeviceParams implements Parcelable {
     *     not shared with other devices' clipboards, including the clipboard of the default device.
     *     <li>{@link #DEVICE_POLICY_CUSTOM}: The device's clipboard is shared with the default
     *     device's clipboard. Any clipboard operation on the virtual device is as if it was done on
     *     the default device.
     *     the default device. Requires all displays of the virtual device to be trusted.
     * </ul>
     *
     * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
     */
    @FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD)
    public static final int POLICY_TYPE_CLIPBOARD = 4;
@@ -821,8 +828,8 @@ public final class VirtualDeviceParams implements Parcelable {
        }

        /**
         * Specifies a component to be used as input method on all displays owned by this virtual
         * device.
         * Specifies a component to be used as input method on all trusted displays owned by this
         * virtual device.
         *
         * @param inputMethodComponent The component name to be used as input method. Must comply to
         *   all general input method requirements described in the guide to
@@ -831,6 +838,7 @@ public final class VirtualDeviceParams implements Parcelable {
         *   may interact with the virtual device, then there will effectively be no IME on this
         *   device's displays for that user.
         *
         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
         * @see android.inputmethodservice.InputMethodService
         * @attr ref android.R.styleable#InputMethod_isVirtualDeviceOnly
         * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker
+4 −1
Original line number Diff line number Diff line
@@ -163,7 +163,6 @@ public abstract class VirtualInputDeviceConfig {
            return self();
        }


        /**
         * Sets the product id of the device, uniquely identifying the device within the address
         * space of a given vendor, identified by the device's vendor id.
@@ -179,6 +178,10 @@ public abstract class VirtualInputDeviceConfig {
         *
         * <p>The input device is restricted to the display with the given ID and may not send
         * events to any other display.</p>
         * <p>The corresponding display must be trusted or mirror display.</p>
         *
         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
         */
        @NonNull
        public T setAssociatedDisplayId(int displayId) {
+78 −22
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.server.companion.virtual;

import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
import static android.companion.virtual.VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
@@ -425,6 +428,27 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        mDisplayManager = displayManager;
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mPowerManager = context.getSystemService(PowerManager.class);

        if (mDevicePolicies.get(POLICY_TYPE_CLIPBOARD, DEVICE_POLICY_DEFAULT)
                != DEVICE_POLICY_DEFAULT) {
            if (mContext.checkCallingOrSelfPermission(ADD_TRUSTED_DISPLAY)
                    != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                        + "set a custom clipboard policy.");
            }
        }

        int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
        if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
            if (mContext.checkCallingOrSelfPermission(ADD_ALWAYS_UNLOCKED_DISPLAY)
                    != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException("Requires ADD_ALWAYS_UNLOCKED_DISPLAY permission to "
                        + "create an always unlocked virtual device.");
            }
            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
        }
        mBaseVirtualDisplayFlags = flags;

        if (inputController == null) {
            mInputController = new InputController(
                    context.getMainThreadHandler(),
@@ -467,12 +491,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                            : mParams.getAllowedActivities();
        }

        int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
        if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
        }
        mBaseVirtualDisplayFlags = flags;

        if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) {
            final String imeId = mParams.getInputMethodComponent().flattenToShortString();
            Slog.d(TAG, "Setting custom input method " + imeId + " as default for virtual device "
@@ -884,8 +902,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                synchronized (mVirtualDeviceLock) {
                    mDevicePolicies.put(policyType, devicePolicy);
                    for (int i = 0; i < mVirtualDisplays.size(); i++) {
                        mVirtualDisplays.valueAt(i).getWindowPolicyController()
                                .setShowInHostDeviceRecents(devicePolicy == DEVICE_POLICY_DEFAULT);
                        VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
                        if (wrapper.isTrusted()) {
                            wrapper.getWindowPolicyController()
                                    .setShowInHostDeviceRecents(
                                            devicePolicy == DEVICE_POLICY_DEFAULT);
                        }
                    }
                }
                break;
@@ -905,7 +927,20 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                break;
            case POLICY_TYPE_CLIPBOARD:
                if (Flags.crossDeviceClipboard()) {
                    if (policyType == DEVICE_POLICY_CUSTOM
                            && mContext.checkCallingOrSelfPermission(ADD_TRUSTED_DISPLAY)
                            != PackageManager.PERMISSION_GRANTED) {
                        throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                                + "set a custom clipboard policy.");
                    }
                    synchronized (mVirtualDeviceLock) {
                        for (int i = 0; i < mVirtualDisplays.size(); i++) {
                            VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
                            if (!wrapper.isTrusted() && !wrapper.isMirror()) {
                                throw new SecurityException("All displays must be trusted for "
                                        + "devices with custom clipboard policy.");
                            }
                        }
                        mDevicePolicies.put(policyType, devicePolicy);
                    }
                }
@@ -936,8 +971,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
            checkDisplayOwnedByVirtualDeviceLocked(displayId);
            switch (policyType) {
                case POLICY_TYPE_RECENTS:
                    mVirtualDisplays.get(displayId).getWindowPolicyController()
                    VirtualDisplayWrapper wrapper = mVirtualDisplays.get(displayId);
                    if (wrapper.isTrusted()) {
                        wrapper.getWindowPolicyController()
                                .setShowInHostDeviceRecents(devicePolicy == DEVICE_POLICY_DEFAULT);
                    }
                    break;
                case POLICY_TYPE_ACTIVITY:
                    mVirtualDisplays.get(displayId).getWindowPolicyController()
@@ -1247,10 +1285,13 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        try {
            synchronized (mVirtualDeviceLock) {
                mDefaultShowPointerIcon = showPointerIcon;
                for (int i = 0; i < mVirtualDisplays.size(); i++) {
                    VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
                    if (wrapper.isTrusted() || wrapper.isMirror()) {
                        mInputController.setShowPointerIcon(
                                mDefaultShowPointerIcon, mVirtualDisplays.keyAt(i));
                    }
                }
            final int[] displayIds = getDisplayIds();
            for (int i = 0; i < displayIds.length; ++i) {
                mInputController.setShowPointerIcon(showPointerIcon, displayIds[i]);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
@@ -1491,6 +1532,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        boolean isTrustedDisplay =
                (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
                        == Display.FLAG_TRUSTED;
        if (!isTrustedDisplay) {
            if (getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) {
                throw new SecurityException("All displays must be trusted for devices with custom"
                        + "clipboard policy.");
            }
        }

        boolean showPointer;
        synchronized (mVirtualDeviceLock) {
@@ -1500,7 +1547,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                        "Virtual device already has a virtual display with ID " + displayId);
            }

            PowerManager.WakeLock wakeLock = createAndAcquireWakeLockForDisplay(displayId);
            PowerManager.WakeLock wakeLock =
                    isTrustedDisplay ? createAndAcquireWakeLockForDisplay(displayId) : null;
            mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock,
                    isTrustedDisplay, isMirrorDisplay));
            showPointer = mDefaultShowPointerIcon;
@@ -1508,14 +1556,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub

        final long token = Binder.clearCallingIdentity();
        try {
            mInputController.setShowPointerIcon(showPointer, displayId);
            mInputController.setMousePointerAccelerationEnabled(false, displayId);
            mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
                    displayId);
            // WM throws a SecurityException if the display is untrusted.
            if (isTrustedDisplay) {
                mInputController.setShowPointerIcon(showPointer, displayId);
                mInputController.setDisplayImePolicy(displayId,
                        WindowManager.DISPLAY_IME_POLICY_LOCAL);
            } else {
                gwpc.setShowInHostDeviceRecents(true);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
@@ -1616,6 +1665,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                    != PackageManager.PERMISSION_GRANTED) {
            synchronized (mVirtualDeviceLock) {
                checkDisplayOwnedByVirtualDeviceLocked(displayId);
                VirtualDisplayWrapper wrapper = mVirtualDisplays.get(displayId);
                if (!wrapper.isTrusted() && !wrapper.isMirror()) {
                    throw new SecurityException(
                            "Cannot create input device associated with an untrusted display");
                }
            }
        }
    }
@@ -1665,7 +1719,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
     * @param virtualDisplayWrapper - VirtualDisplayWrapper to release resources for.
     */
    private void releaseOwnedVirtualDisplayResources(VirtualDisplayWrapper virtualDisplayWrapper) {
        virtualDisplayWrapper.getWakeLock().release();
        virtualDisplayWrapper.releaseWakeLock();
        virtualDisplayWrapper.getWindowPolicyController().unregisterRunningAppsChangedListener(
                this);
    }
@@ -1833,10 +1887,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub

        VirtualDisplayWrapper(@NonNull IVirtualDisplayCallback token,
                @NonNull GenericWindowPolicyController windowPolicyController,
                @NonNull PowerManager.WakeLock wakeLock, boolean isTrusted, boolean isMirror) {
                @Nullable PowerManager.WakeLock wakeLock, boolean isTrusted, boolean isMirror) {
            mToken = Objects.requireNonNull(token);
            mWindowPolicyController = Objects.requireNonNull(windowPolicyController);
            mWakeLock = Objects.requireNonNull(wakeLock);
            mWakeLock = wakeLock;
            mIsTrusted = isTrusted;
            mIsMirror = isMirror;
        }
@@ -1845,8 +1899,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
            return mWindowPolicyController;
        }

        PowerManager.WakeLock getWakeLock() {
            return mWakeLock;
        void releaseWakeLock() {
            if (mWakeLock != null && mWakeLock.isHeld()) {
                mWakeLock.release();
            }
        }

        boolean isTrusted() {
+4 −5
Original line number Diff line number Diff line
@@ -1769,10 +1769,11 @@ public final class DisplayManagerService extends SystemService {
            flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
        }
        // Put the display in the virtual device's display group only if it's not a mirror display,
        // and if it doesn't need its own display group. So effectively, mirror displays go into the
        // default display group.
        // it is a trusted display, and it doesn't need its own display group. So effectively,
        // mirror and untrusted displays go into the default display group.
        if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) == 0
                && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0
                && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == VIRTUAL_DISPLAY_FLAG_TRUSTED
                && virtualDevice != null) {
            flags |= VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP;
        }
@@ -1848,9 +1849,7 @@ public final class DisplayManagerService extends SystemService {

        if (callingUid != Process.SYSTEM_UID
                && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
            // The virtualDevice instance has been validated above using isValidVirtualDevice
            if (virtualDevice == null
                    && !checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
            if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
                throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                        + "create a virtual display which is not in the default DisplayGroup.");
            }
Loading