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