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);