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
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 21427ac..55ae8ee 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -56,3 +56,10 @@
   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"
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 852e36d..8c728f1 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -118,6 +118,7 @@
     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 @@
     /**
      * 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 @@
             @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 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 @@
             // 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 @@
                     && 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,
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 4298c07..70c449f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -167,7 +167,8 @@
     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 @@
     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 @@
         mParams = params;
         mDevicePolicies = params.getDevicePolicies();
         mDisplayManager = displayManager;
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         if (inputController == null) {
             mInputController = new InputController(
                     context.getMainThreadHandler(),
@@ -971,8 +974,8 @@
             @NonNull Set<String> displayCategories) {
         final boolean activityLaunchAllowedByDefault =
                 Flags.dynamicPolicy()
-                        ? getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
-                        : mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED;
+                    ? getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
+                    : mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED;
         final boolean crossTaskNavigationAllowedByDefault =
                 mParams.getDefaultNavigationPolicy() == NAVIGATION_POLICY_DEFAULT_ALLOWED;
         final boolean showTasksInHostDeviceRecents =
@@ -987,7 +990,7 @@
                 activityLaunchAllowedByDefault,
                 mActivityPolicyExemptions,
                 crossTaskNavigationAllowedByDefault,
-                /*crossTaskNavigationExemptions=*/crossTaskNavigationAllowedByDefault
+                /* crossTaskNavigationExemptions= */crossTaskNavigationAllowedByDefault
                         ? mParams.getBlockedCrossTaskNavigations()
                         : mParams.getAllowedCrossTaskNavigations(),
                 mPermissionDialogComponent,
@@ -1016,12 +1019,12 @@
         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) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 087cf20..e475fe6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -31,6 +31,7 @@
 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.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 @@
         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 @@
 
         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 @@
             }
         }
 
+        // 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 @@
         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) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 16d72e4..163d248 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -18,10 +18,13 @@
 
 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.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.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 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 @@
     @Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
     @Mock PackageManagerInternal mMockPackageManagerInternal;
 
-
     @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
     @Mock DisplayManagerFlags mMockFlags;
 
@@ -320,6 +326,7 @@
     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 @@
                 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);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index a7c8a6c..b732d38 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -150,7 +150,7 @@
     @Test
     public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -163,7 +163,7 @@
     @Test
     public void activityDoesNotSupportDisplayOnRemoteDevices_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -176,7 +176,7 @@
     @Test
     public void openBlockedComponentOnVirtualDisplay_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent(BLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_PACKAGE_NAME,
@@ -189,7 +189,7 @@
     @Test
     public void addActivityPolicyExemption_openBlockedOnVirtualDisplay_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
         gwpc.setActivityLaunchDefaultAllowed(true);
         gwpc.addActivityPolicyExemption(BLOCKED_COMPONENT);
 
@@ -204,7 +204,7 @@
     @Test
     public void openNotAllowedComponentOnBlocklistVirtualDisplay_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_PACKAGE_NAME,
@@ -217,7 +217,7 @@
     @Test
     public void addActivityPolicyExemption_openNotAllowedOnVirtualDisplay_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
         gwpc.setActivityLaunchDefaultAllowed(false);
         gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
 
@@ -232,7 +232,7 @@
     @Test
     public void openAllowedComponentOnBlocklistVirtualDisplay_startsActivity() {
         GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -245,7 +245,7 @@
     @Test
     public void addActivityPolicyExemption_openAllowedOnVirtualDisplay_startsActivity() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
         gwpc.setActivityLaunchDefaultAllowed(false);
         gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
 
@@ -258,9 +258,22 @@
     }
 
     @Test
+    public void openNonBlockedAppOnMirrorVirtualDisplay_isBlocked() {
+        GenericWindowPolicyController gwpc = createGwpc();
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ true);
+
+        ActivityInfo activityInfo = getActivityInfo(
+                NONBLOCKED_APP_PACKAGE_NAME,
+                NONBLOCKED_APP_PACKAGE_NAME,
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+        assertNoActivityLaunched(gwpc, DISPLAY_ID, activityInfo);
+    }
+
+    @Test
     public void canActivityBeLaunched_mismatchingUserHandle_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -274,7 +287,7 @@
     @Test
     public void canActivityBeLaunched_blockedAppStreamingComponent_isNeverBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
@@ -288,7 +301,7 @@
     public void canActivityBeLaunched_blockedAppStreamingComponentExplicitlyBlocked_isNeverBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent(
                 BLOCKED_APP_STREAMING_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
@@ -302,7 +315,7 @@
     @Test
     public void canActivityBeLaunched_blockedAppStreamingComponentExemptFromStreaming_isNeverBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
         gwpc.setActivityLaunchDefaultAllowed(true);
         gwpc.addActivityPolicyExemption(BLOCKED_APP_STREAMING_COMPONENT);
 
@@ -318,7 +331,7 @@
     @Test
     public void canActivityBeLaunched_blockedAppStreamingComponentNotAllowlisted_isNeverBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
@@ -332,7 +345,7 @@
     @Test
     public void canActivityBeLaunched_blockedAppStreamingComponentNotExemptFromBlocklist_isNeverBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
         gwpc.setActivityLaunchDefaultAllowed(false);
         gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
 
@@ -348,7 +361,7 @@
     @Test
     public void canActivityBeLaunched_customDisplayCategoryMatches_isNotBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -362,7 +375,7 @@
     @Test
     public void canActivityBeLaunched_customDisplayCategoryDoesNotMatch_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -375,7 +388,7 @@
     @Test
     public void canActivityBeLaunched_crossTaskLaunch_fromDefaultDisplay_isNotBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -390,7 +403,7 @@
     public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notExplicitlyBlocked_isNotBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor(
                 BLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -406,7 +419,7 @@
     public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_explicitlyBlocked_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor(
                 BLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_PACKAGE_NAME,
@@ -421,7 +434,7 @@
     public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notAllowed_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed(
                 NONBLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_PACKAGE_NAME,
@@ -436,7 +449,7 @@
     public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_allowed_isNotBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed(
                 NONBLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -450,7 +463,7 @@
     @Test
     public void canActivityBeLaunched_unsupportedWindowingMode_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -464,7 +477,7 @@
     @Test
     public void canActivityBeLaunched_permissionComponent_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithPermissionComponent(BLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 BLOCKED_PACKAGE_NAME,
@@ -490,7 +503,7 @@
     public void onRunningAppsChanged_empty_onDisplayEmpty() {
         ArraySet<Integer> uids = new ArraySet<>();
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         gwpc.onRunningAppsChanged(uids);
 
@@ -585,7 +598,7 @@
     public void onTopActivitychanged_activityListenerCallbackObserved() {
         int userId = 1000;
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         gwpc.onTopActivityChanged(BLOCKED_COMPONENT, 0, userId);
         verify(mActivityListener)
@@ -595,7 +608,7 @@
     @Test
     public void keepActivityOnWindowFlagsChanged_noChange() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -613,7 +626,7 @@
     @Test
     public void keepActivityOnWindowFlagsChanged_flagSecure_isAllowedAfterTM() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -631,7 +644,7 @@
     @Test
     public void keepActivityOnWindowFlagsChanged_systemFlagHideNonSystemOverlayWindows_isAllowedAfterTM() {
         GenericWindowPolicyController gwpc = createGwpc();
-        gwpc.setDisplayId(DISPLAY_ID);
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
 
         ActivityInfo activityInfo = getActivityInfo(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -887,4 +900,14 @@
         verify(mActivityBlockedCallback).onActivityBlocked(fromDisplay, activityInfo);
         verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
     }
+
+    private void assertNoActivityLaunched(GenericWindowPolicyController gwpc, int fromDisplay,
+            ActivityInfo activityInfo) {
+        assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
+                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, true))
+                .isFalse();
+
+        verify(mActivityBlockedCallback, never()).onActivityBlocked(fromDisplay, activityInfo);
+        verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index a3d415e..461d637 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -59,7 +59,6 @@
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
 import android.companion.virtual.flags.Flags;
-import android.companion.virtual.sensor.IVirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensor;
 import android.companion.virtual.sensor.VirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensorConfig;
@@ -250,8 +249,6 @@
     @Mock
     private SensorManagerInternal mSensorManagerInternalMock;
     @Mock
-    private IVirtualSensorCallback mVirtualSensorCallback;
-    @Mock
     private VirtualSensorCallback mSensorCallback;
     @Mock
     private IVirtualDeviceActivityListener mActivityListener;
@@ -269,7 +266,6 @@
     IPowerManager mIPowerManagerMock;
     @Mock
     IThermalService mIThermalServiceMock;
-    private PowerManager mPowerManager;
     @Mock
     private IAudioRoutingCallback mRoutingCallback;
     @Mock
@@ -361,9 +357,10 @@
         when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
                 mDevicePolicyManagerMock);
 
-        mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock,
+        PowerManager powerManager = new PowerManager(mContext, mIPowerManagerMock,
+                mIThermalServiceMock,
                 new Handler(TestableLooper.get(this).getLooper()));
-        when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
+        when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
 
         mInputManagerMockHelper = new InputManagerMockHelper(
                 TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock);
@@ -1604,6 +1601,54 @@
     }
 
     @Test
+    public void openNonBlockedAppOnMirrorDisplay_flagEnabled_cannotBeLaunched() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
+        when(mDisplayManagerInternalMock.getDisplayIdToMirror(anyInt()))
+                .thenReturn(Display.DEFAULT_DISPLAY);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+                DISPLAY_ID_1);
+        doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+
+        ActivityInfo activityInfo = getActivityInfo(
+                NONBLOCKED_APP_PACKAGE_NAME,
+                NONBLOCKED_APP_PACKAGE_NAME,
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+        assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
+                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/ false))
+                .isFalse();
+        // Verify that BlockedAppStreamingActivity also doesn't launch for mirror displays.
+        Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
+                activityInfo, mAssociationInfo.getDisplayName());
+        verify(mContext, never()).startActivityAsUser(argThat(intent ->
+                intent.filterEquals(blockedAppIntent)), any(), any());
+    }
+
+    @Test
+    public void openNonBlockedAppOnMirrorDisplay_flagDisabled_launchesActivity() {
+        when(mDisplayManagerInternalMock.getDisplayIdToMirror(anyInt()))
+                .thenReturn(Display.DEFAULT_DISPLAY);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+                DISPLAY_ID_1);
+        doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+
+        ActivityInfo activityInfo = getActivityInfo(
+                NONBLOCKED_APP_PACKAGE_NAME,
+                NONBLOCKED_APP_PACKAGE_NAME,
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+        assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
+                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/ false))
+                .isTrue();
+        Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
+                activityInfo, mAssociationInfo.getDisplayName());
+        verify(mContext, never()).startActivityAsUser(argThat(intent ->
+                intent.filterEquals(blockedAppIntent)), any(), any());
+    }
+
+    @Test
     public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);