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

Commit be39353a authored by Biswarup Pal's avatar Biswarup Pal
Browse files

VDM interactive screen mirroring

- Allow VDM to create auto mirror virtual displays:
It would be up to the caller to pass the appropriate
flags to VirtualDevice#createVirtualDisplay to create
an auto-mirror virtual display, following the exact
semantics as DisplayManager#createVirtualDisplay, i.e.,
either pass VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR explicitly
or pass VIRTUAL_DISPLAY_FLAG_PUBLIC, without
VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY in either case.

- VDM supports mirroring only the default display, and
any mirror display is put into the default display group.

- Launching presentations or activities are not allowed
on any mirror display created by a virtual device.

- VIRTUAL_DISPLAY_ALWAYS_UNLOCKED flag is not set for
any mirror display.

Test: atest DisplayManagerServiceTest, VirtualDeviceManagerServiceTest
Bug: 292212199
Change-Id: I3f65f1f19bd44aba278531308822c05da956f700
parent c30046eb
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -56,3 +56,10 @@ flag {
  description: "Enable express metrics in VDM"
  bug: "307297730"
}

flag {
  name: "interactive_screen_mirror"
  namespace: "virtual_devices"
  description: "Enable interactive screen mirroring using Virtual Devices"
  bug: "292212199"
}
+18 −7
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
    private final Object mGenericWindowPolicyControllerLock = new Object();
    @Nullable private final ActivityBlockedCallback mActivityBlockedCallback;
    private int mDisplayId = Display.INVALID_DISPLAY;
    private boolean mIsMirrorDisplay = false;

    @NonNull
    @GuardedBy("mGenericWindowPolicyControllerLock")
@@ -203,8 +204,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
    /**
     * Expected to be called once this object is associated with a newly created display.
     */
    public void setDisplayId(int displayId) {
    void setDisplayId(int displayId, boolean isMirrorDisplay) {
        mDisplayId = displayId;
        mIsMirrorDisplay = isMirrorDisplay;
    }

    /**
@@ -256,9 +258,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
            @Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
            int launchingFromDisplayId, boolean isNewTask) {
        if (!canContainActivity(activityInfo, windowingMode, launchingFromDisplayId, isNewTask)) {
            if (mActivityBlockedCallback != null) {
                mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
            }
            notifyActivityBlocked(activityInfo);
            return false;
        }
        if (mIntentListenerCallback != null && intent != null
@@ -273,6 +273,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
    public boolean canContainActivity(@NonNull ActivityInfo activityInfo,
            @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
            boolean isNewTask) {
        // Mirror displays cannot contain activities.
        if (mIsMirrorDisplay) {
            Slog.d(TAG, "Mirror virtual displays cannot contain activities.");
            return false;
        }
        if (!isWindowingModeSupported(windowingMode)) {
            Slog.d(TAG, "Virtual device doesn't support windowing mode " + windowingMode);
            return false;
@@ -344,9 +349,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
            // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
            if ((windowFlags & FLAG_SECURE) != 0
                    || (systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
                if (mActivityBlockedCallback != null) {
                    mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
                }
                notifyActivityBlocked(activityInfo);
                return false;
            }
        }
@@ -428,6 +431,14 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
                    && mDisplayCategories.contains(activityInfo.requiredDisplayCategory);
    }

    private void notifyActivityBlocked(ActivityInfo activityInfo) {
        // Don't trigger activity blocked callback for mirror displays, because we can't show
        // any activity or presentation on it anyway.
        if (!mIsMirrorDisplay && mActivityBlockedCallback != null) {
            mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
        }
    }

    private static boolean isAllowedByPolicy(boolean allowedByDefault,
            Set<ComponentName> exemptions, ComponentName component) {
        // Either allowed and the exemptions do not contain the component,
+11 −8
Original line number Diff line number Diff line
@@ -167,7 +167,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    private final VirtualDeviceLog mVirtualDeviceLog;
    private final String mOwnerPackageName;
    private final int mDeviceId;
    private @Nullable final String mPersistentDeviceId;
    @Nullable
    private final String mPersistentDeviceId;
    // Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
    // Holding the lock can lead to lock inversion with GlobalWindowManagerLock.
    // 1. After display is created the window manager calls into VDM during construction
@@ -191,6 +192,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    private final IVirtualDeviceActivityListener mActivityListener;
    private final IVirtualDeviceSoundEffectListener mSoundEffectListener;
    private final DisplayManagerGlobal mDisplayManager;
    private final DisplayManagerInternal mDisplayManagerInternal;
    @GuardedBy("mVirtualDeviceLock")
    private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
    @NonNull
@@ -313,6 +315,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        mParams = params;
        mDevicePolicies = params.getDevicePolicies();
        mDisplayManager = displayManager;
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        if (inputController == null) {
            mInputController = new InputController(
                    context.getMainThreadHandler(),
@@ -1016,12 +1019,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        synchronized (mVirtualDeviceLock) {
            gwpc = createWindowPolicyControllerLocked(virtualDisplayConfig.getDisplayCategories());
        }
        DisplayManagerInternal displayManager = LocalServices.getService(
                DisplayManagerInternal.class);
        int displayId;
        displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback,
        displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig, callback,
                this, gwpc, packageName);
        gwpc.setDisplayId(displayId);
        gwpc.setDisplayId(displayId, /* isMirrorDisplay= */ Flags.interactiveScreenMirror()
                && mDisplayManagerInternal.getDisplayIdToMirror(displayId)
                    != Display.INVALID_DISPLAY);

        boolean showPointer;
        synchronized (mVirtualDeviceLock) {
+28 −3
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_S
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -57,6 +58,7 @@ import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.flags.Flags;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
@@ -1482,7 +1484,12 @@ public final class DisplayManagerService extends SystemService {
        if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
            flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
        }
        if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) == 0 && virtualDevice != null) {
        // 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.
        if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) == 0
                && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0
                && virtualDevice != null) {
            flags |= VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP;
        }

@@ -1516,11 +1523,16 @@ public final class DisplayManagerService extends SystemService {

        if (callingUid != Process.SYSTEM_UID
                && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
            if (!canProjectVideo(projection)) {
            // Only a valid media projection or a virtual device can create a mirror virtual
            // display.
            if (!canProjectVideo(projection)
                    && !isMirroringSupportedByVirtualDevice(virtualDevice)) {
                throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
                        + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
                        + "MediaProjection token in order to create a screen sharing virtual "
                        + "display.");
                        + "display. In order to create a virtual display that does not perform"
                        + "screen sharing (mirroring), please use the flag"
                        + "VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY.");
            }
        }
        if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
@@ -1540,6 +1552,15 @@ public final class DisplayManagerService extends SystemService {
            }
        }

        // Mirror virtual displays created by a virtual device are not allowed to show
        // presentations.
        if (virtualDevice != null && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0
                && (flags & VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) {
            Slog.d(TAG, "Mirror displays created by a virtual device cannot show "
                    + "presentations, hence ignoring flag VIRTUAL_DISPLAY_FLAG_PRESENTATION.");
            flags &= ~VIRTUAL_DISPLAY_FLAG_PRESENTATION;
        }

        if (callingUid != Process.SYSTEM_UID
                && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
            // The virtualDevice instance has been validated above using isValidVirtualDevice
@@ -1739,6 +1760,10 @@ public final class DisplayManagerService extends SystemService {
        return -1;
    }

    private static boolean isMirroringSupportedByVirtualDevice(IVirtualDevice virtualDevice) {
        return Flags.interactiveScreenMirror() && virtualDevice != null;
    }

    private void resizeVirtualDisplayInternal(IBinder appToken,
            int width, int height, int densityDpi) {
        synchronized (mSyncRoot) {
+238 −1
Original line number Diff line number Diff line
@@ -18,10 +18,13 @@ package com.android.server.display;

import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;

@@ -60,6 +63,7 @@ import android.app.PropertyInvalidatedCache;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceManager;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.flags.Flags;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.ContextWrapper;
@@ -92,6 +96,7 @@ import android.os.Looper;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayCutout;
@@ -181,6 +186,8 @@ public class DisplayManagerServiceTest {
    public TestRule compatChangeRule = new PlatformCompatChangeRule();
    @Rule(order = 1)
    public Expect expect = Expect.create();
    @Rule
    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private Context mContext;

@@ -312,7 +319,6 @@ public class DisplayManagerServiceTest {
    @Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
    @Mock PackageManagerInternal mMockPackageManagerInternal;


    @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
    @Mock DisplayManagerFlags mMockFlags;

@@ -320,6 +326,7 @@ public class DisplayManagerServiceTest {
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
        mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);

        LocalServices.removeServiceForTest(InputManagerInternal.class);
        LocalServices.addService(InputManagerInternal.class, mMockInputManagerInternal);
@@ -1140,6 +1147,236 @@ public class DisplayManagerServiceTest {
                0);
    }

    /**
     * Tests that it's not allowed to create an auto-mirror virtual display without
     * CAPTURE_VIDEO_OUTPUT permission or a virtual device.
     */
    @Test
    public void createAutoMirrorDisplay_withoutPermission_withoutVirtualDevice_throwsException() {
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
        DisplayManagerInternal localService = displayManager.new LocalService();
        registerDefaultDisplays(displayManager);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        when(mContext.checkCallingPermission(CAPTURE_VIDEO_OUTPUT)).thenReturn(
                PackageManager.PERMISSION_DENIED);

        final VirtualDisplayConfig.Builder builder =
                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR)
                        .setUniqueId("uniqueId --- mirror display");
        assertThrows(SecurityException.class, () -> {
            localService.createVirtualDisplay(
                            builder.build(),
                            mMockAppToken /* callback */,
                            null /* virtualDeviceToken */,
                            mock(DisplayWindowPolicyController.class),
                            PACKAGE_NAME);
        });
    }

    /**
     * Tests that it's not allowed to create an auto-mirror virtual display when display mirroring
     * is not supported in a virtual device.
     */
    @Test
    public void createAutoMirrorDisplay_virtualDeviceDoesntSupportMirroring_throwsException()
            throws Exception {
        mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
        DisplayManagerInternal localService = displayManager.new LocalService();
        registerDefaultDisplays(displayManager);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        when(mContext.checkCallingPermission(CAPTURE_VIDEO_OUTPUT)).thenReturn(
                PackageManager.PERMISSION_DENIED);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);

        final VirtualDisplayConfig.Builder builder =
                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR)
                        .setUniqueId("uniqueId --- mirror display");
        assertThrows(SecurityException.class, () -> {
            localService.createVirtualDisplay(
                    builder.build(),
                    mMockAppToken /* callback */,
                    virtualDevice /* virtualDeviceToken */,
                    mock(DisplayWindowPolicyController.class),
                    PACKAGE_NAME);
        });
    }

    /**
     * Tests that the virtual display is added to the default display group when created with
     * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
     */
    @Test
    public void createAutoMirrorVirtualDisplay_addsDisplayToDefaultDisplayGroup() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
        DisplayManagerInternal localService = displayManager.new LocalService();
        registerDefaultDisplays(displayManager);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);

        // Create an auto-mirror virtual display using a virtual device.
        final VirtualDisplayConfig.Builder builder =
                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR)
                        .setUniqueId("uniqueId --- default display group");
        int displayId =
                localService.createVirtualDisplay(
                        builder.build(),
                        mMockAppToken /* callback */,
                        virtualDevice /* virtualDeviceToken */,
                        mock(DisplayWindowPolicyController.class),
                        PACKAGE_NAME);

        // The virtual display should be in the default display group.
        assertEquals(Display.DEFAULT_DISPLAY_GROUP,
                localService.getDisplayInfo(displayId).displayGroupId);
    }

    /**
     * Tests that the virtual display mirrors the default display when created with
     * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
     */
    @Test
    public void createAutoMirrorVirtualDisplay_mirrorsDefaultDisplay() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
        DisplayManagerInternal localService = displayManager.new LocalService();
        registerDefaultDisplays(displayManager);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);

        // Create an auto-mirror virtual display using a virtual device.
        final VirtualDisplayConfig.Builder builder =
                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR)
                        .setUniqueId("uniqueId --- mirror display");
        int displayId =
                localService.createVirtualDisplay(
                        builder.build(),
                        mMockAppToken /* callback */,
                        virtualDevice /* virtualDeviceToken */,
                        mock(DisplayWindowPolicyController.class),
                        PACKAGE_NAME);

        // The virtual display should mirror the default display.
        assertEquals(Display.DEFAULT_DISPLAY, localService.getDisplayIdToMirror(displayId));
    }

    /**
     * Tests that the virtual display does not mirror any other display when created with
     * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY using a virtual device.
     */
    @Test
    public void createOwnContentOnlyVirtualDisplay_doesNotMirrorAnyDisplay() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
        DisplayManagerInternal localService = displayManager.new LocalService();
        registerDefaultDisplays(displayManager);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);

        // Create an auto-mirror virtual display using a virtual device.
        final VirtualDisplayConfig.Builder builder =
                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                        .setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY)
                        .setUniqueId("uniqueId --- own content only display");
        int displayId =
                localService.createVirtualDisplay(
                        builder.build(),
                        mMockAppToken /* callback */,
                        virtualDevice /* virtualDeviceToken */,
                        mock(DisplayWindowPolicyController.class),
                        PACKAGE_NAME);

        // The virtual display should not mirror any display.
        assertEquals(Display.INVALID_DISPLAY, localService.getDisplayIdToMirror(displayId));
        // The virtual display should have FLAG_OWN_CONTENT_ONLY set.
        assertEquals(DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY,
                (displayManager.getDisplayDeviceInfoInternal(displayId).flags
                        & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY));
    }

    /**
     * Tests that the virtual display should not be always unlocked when created with
     * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
     */
    @Test
    public void createAutoMirrorVirtualDisplay_flagAlwaysUnlockedNotSet() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
        DisplayManagerInternal localService = displayManager.new LocalService();
        registerDefaultDisplays(displayManager);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
        when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY))
                .thenReturn(PackageManager.PERMISSION_GRANTED);

        // Create an auto-mirror virtual display using a virtual device.
        final VirtualDisplayConfig.Builder builder =
                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
                                | VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED)
                        .setUniqueId("uniqueId --- mirror display");
        int displayId =
                localService.createVirtualDisplay(
                        builder.build(),
                        mMockAppToken /* callback */,
                        virtualDevice /* virtualDeviceToken */,
                        mock(DisplayWindowPolicyController.class),
                        PACKAGE_NAME);

        // The virtual display should not have FLAG_ALWAYS_UNLOCKED set.
        assertEquals(0, (displayManager.getDisplayDeviceInfoInternal(displayId).flags
                        & DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED));
    }

    /**
     * Tests that the virtual display should not allow presentation when created with
     * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
     */
    @Test
    public void createAutoMirrorVirtualDisplay_flagPresentationNotSet() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
        DisplayManagerInternal localService = displayManager.new LocalService();
        registerDefaultDisplays(displayManager);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);

        // Create an auto-mirror virtual display using a virtual device.
        final VirtualDisplayConfig.Builder builder =
                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
                                | VIRTUAL_DISPLAY_FLAG_PRESENTATION)
                        .setUniqueId("uniqueId --- mirror display");
        int displayId =
                localService.createVirtualDisplay(
                        builder.build(),
                        mMockAppToken /* callback */,
                        virtualDevice /* virtualDeviceToken */,
                        mock(DisplayWindowPolicyController.class),
                        PACKAGE_NAME);

        // The virtual display should not have FLAG_PRESENTATION set.
        assertEquals(0, (displayManager.getDisplayDeviceInfoInternal(displayId).flags
                        & DisplayDeviceInfo.FLAG_PRESENTATION));
    }

    @Test
    public void testGetDisplayIdToMirror() throws Exception {
        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
Loading