Merge "Only allow mirroring on a VirtualDisplay with a mirroring flag or MediaProjection instance." into udc-dev
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ea157c8..e01aa9b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1514,14 +1514,17 @@
}
}
- // When calling setContentRecordingSession into the WindowManagerService, the WMS
+ // When calling WindowManagerService#setContentRecordingSession, WindowManagerService
// attempts to acquire a lock before executing its main body. Due to this, we need
// to be sure that it isn't called while the DisplayManagerService is also holding
// a lock, to avoid a deadlock scenario.
final ContentRecordingSession session =
virtualDisplayConfig.getContentRecordingSession();
-
- if (displayId != Display.INVALID_DISPLAY && session != null) {
+ // Ensure session details are only set when mirroring (through VirtualDisplay flags or
+ // MediaProjection).
+ final boolean shouldMirror =
+ projection != null || (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0;
+ if (shouldMirror && displayId != Display.INVALID_DISPLAY && session != null) {
// Only attempt to set content recording session if there are details to set and a
// VirtualDisplay has been successfully constructed.
session.setDisplayId(displayId);
@@ -1529,8 +1532,8 @@
// We set the content recording session here on the server side instead of using
// a second AIDL call in MediaProjection. By ensuring that a virtual display has
// been constructed before calling setContentRecordingSession, we avoid a race
- // condition between the DMS & WMS which could lead to the MediaProjection
- // being pre-emptively torn down.
+ // condition between the DisplayManagerService & WindowManagerService which could
+ // lead to the MediaProjection being pre-emptively torn down.
if (!mWindowManagerInternal.setContentRecordingSession(session)) {
// Unable to start mirroring, so tear down projection & release VirtualDisplay.
try {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 94d30bb..6d2ce7f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -34,7 +34,9 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -65,12 +67,14 @@
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
+import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
import android.os.Process;
+import android.os.RemoteException;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayCutout;
@@ -1024,11 +1028,14 @@
}
@Test
- public void testCreateVirtualDisplay_setContentRecordingSessionSuccess() throws Exception {
+ public void testCreateVirtualDisplay_setContentRecordingSessionSuccess()
+ throws RemoteException {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mMockWindowManagerInternal
.setContentRecordingSession(any(ContentRecordingSession.class)))
.thenReturn(true);
+ IMediaProjection projection = mock(IMediaProjection.class);
+ doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection));
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
@@ -1042,17 +1049,19 @@
DisplayManagerService.BinderService binderService = displayManager.new BinderService();
final int displayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, projection, PACKAGE_NAME);
assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
}
@Test
- public void testCreateVirtualDisplay_setContentRecordingSessionFail() throws Exception {
+ public void testCreateVirtualDisplay_setContentRecordingSessionFail() throws RemoteException {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mMockWindowManagerInternal
.setContentRecordingSession(any(ContentRecordingSession.class)))
.thenReturn(false);
+ IMediaProjection projection = mock(IMediaProjection.class);
+ doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection));
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
@@ -1066,11 +1075,96 @@
DisplayManagerService.BinderService binderService = displayManager.new BinderService();
final int displayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, projection, PACKAGE_NAME);
assertThat(displayId).isEqualTo(Display.INVALID_DISPLAY);
}
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSession_noProjection_noFlags() {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+ // Set no flags for the VirtualDisplay.
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ // Pass in a null projection.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ // VirtualDisplay is created but not for mirroring.
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSession_noProjection_noMirroringFlag() {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+ // Set a non-mirroring flag for the VirtualDisplay.
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ // Pass in a null projection.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ // VirtualDisplay is created but not for mirroring.
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSession_projection_noMirroringFlag()
+ throws RemoteException {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockWindowManagerInternal
+ .setContentRecordingSession(any(ContentRecordingSession.class)))
+ .thenReturn(true);
+ IMediaProjection projection = mock(IMediaProjection.class);
+ doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection));
+
+ // Set no flags for the VirtualDisplay.
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ // Pass in a non-null projection.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, projection, PACKAGE_NAME);
+
+ // VirtualDisplay is created for mirroring.
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ }
+
/**
* Tests that the virtual display is created with
* {@link VirtualDisplayConfig.Builder#setSurface(Surface)}