From 17469b9ac364a47296707c04016a602091a0ebbe Mon Sep 17 00:00:00 2001 From: dakinola Date: Wed, 8 Feb 2023 17:55:07 +0000 Subject: [PATCH] VirtualDisplay: Merge AIDL calls to stop race condition Updating execution flow so that setting ContentRecordingSession is no longer a second AIDL call to the DisplayManager Service, but instead passing in the session details with the VirtualDisplayConfig, and having it be set manually in the execution of an earlier AIDL call. Bug: 267610691 Fix: 267610691 Test: manually built Test: atest CtsWindowManagerDeviceTestCases:ActivityCaptureCallbackTests Test: atest CtsWindowManagerDeviceTestCases:DisplayTests Test: atest DisplayManagerServiceTest Test: atest DisplayTest Test: atest WmTests:ContentRecorderTests Change-Id: Id926aa0800bd23d329bbc8f2a6f739e26fd72a3c --- .../display/VirtualDisplayConfig.java | 39 +++++- .../media/projection/MediaProjection.java | 13 +- .../server/display/DisplayManagerService.java | 46 ++++++- .../server/display/VirtualDisplayAdapter.java | 21 ++- .../tests/servicestests/AndroidManifest.xml | 1 + .../display/DisplayManagerServiceTest.java | 126 +++++++++++++++--- 6 files changed, 209 insertions(+), 37 deletions(-) diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java index 067ae4d438ccd..490e55ba260fc 100644 --- a/core/java/android/hardware/display/VirtualDisplayConfig.java +++ b/core/java/android/hardware/display/VirtualDisplayConfig.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; +import android.view.ContentRecordingSession; import android.view.Display; import android.view.Surface; @@ -53,6 +54,8 @@ public final class VirtualDisplayConfig implements Parcelable { private final int mDisplayIdToMirror; private final boolean mWindowManagerMirroringEnabled; private ArraySet mDisplayCategories = null; + @Nullable + private ContentRecordingSession mContentRecordingSession; private final float mRequestedRefreshRate; private VirtualDisplayConfig( @@ -65,6 +68,7 @@ public final class VirtualDisplayConfig implements Parcelable { @Nullable String uniqueId, int displayIdToMirror, boolean windowManagerMirroringEnabled, + ContentRecordingSession session, @NonNull ArraySet displayCategories, float requestedRefreshRate) { mName = name; @@ -76,6 +80,7 @@ public final class VirtualDisplayConfig implements Parcelable { mUniqueId = uniqueId; mDisplayIdToMirror = displayIdToMirror; mWindowManagerMirroringEnabled = windowManagerMirroringEnabled; + mContentRecordingSession = session; mDisplayCategories = displayCategories; mRequestedRefreshRate = requestedRefreshRate; } @@ -155,6 +160,17 @@ public final class VirtualDisplayConfig implements Parcelable { return mWindowManagerMirroringEnabled; } + /** + * Returns the recording session associated with this VirtualDisplay. Only used for + * recording via {@link MediaProjection}. + * + * @hide + */ + @Nullable + public ContentRecordingSession getContentRecordingSession() { + return mContentRecordingSession; + } + /** * Returns the display categories. * @@ -186,6 +202,7 @@ public final class VirtualDisplayConfig implements Parcelable { dest.writeString8(mUniqueId); dest.writeInt(mDisplayIdToMirror); dest.writeBoolean(mWindowManagerMirroringEnabled); + dest.writeTypedObject(mContentRecordingSession, flags); dest.writeArraySet(mDisplayCategories); dest.writeFloat(mRequestedRefreshRate); } @@ -211,6 +228,7 @@ public final class VirtualDisplayConfig implements Parcelable { && Objects.equals(mUniqueId, that.mUniqueId) && mDisplayIdToMirror == that.mDisplayIdToMirror && mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled + && Objects.equals(mContentRecordingSession, that.mContentRecordingSession) && Objects.equals(mDisplayCategories, that.mDisplayCategories) && mRequestedRefreshRate == that.mRequestedRefreshRate; } @@ -219,8 +237,8 @@ public final class VirtualDisplayConfig implements Parcelable { public int hashCode() { int hashCode = Objects.hash( mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId, - mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories, - mRequestedRefreshRate); + mDisplayIdToMirror, mWindowManagerMirroringEnabled, mContentRecordingSession, + mDisplayCategories, mRequestedRefreshRate); return hashCode; } @@ -237,6 +255,7 @@ public final class VirtualDisplayConfig implements Parcelable { + " mUniqueId=" + mUniqueId + " mDisplayIdToMirror=" + mDisplayIdToMirror + " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled + + " mContentRecordingSession=" + mContentRecordingSession + " mDisplayCategories=" + mDisplayCategories + " mRequestedRefreshRate=" + mRequestedRefreshRate + ")"; @@ -252,6 +271,7 @@ public final class VirtualDisplayConfig implements Parcelable { mUniqueId = in.readString8(); mDisplayIdToMirror = in.readInt(); mWindowManagerMirroringEnabled = in.readBoolean(); + mContentRecordingSession = in.readTypedObject(ContentRecordingSession.CREATOR); mDisplayCategories = (ArraySet) in.readArraySet(null); mRequestedRefreshRate = in.readFloat(); } @@ -283,6 +303,8 @@ public final class VirtualDisplayConfig implements Parcelable { private String mUniqueId = null; private int mDisplayIdToMirror = DEFAULT_DISPLAY; private boolean mWindowManagerMirroringEnabled = false; + @Nullable + private ContentRecordingSession mContentRecordingSession; private ArraySet mDisplayCategories = new ArraySet<>(); private float mRequestedRefreshRate = 0.0f; @@ -374,6 +396,18 @@ public final class VirtualDisplayConfig implements Parcelable { return this; } + /** + * Sets the recording session associated with this {@link VirtualDisplay}. Only used for + * recording via {@link MediaProjection}. + * + * @hide + */ + @NonNull + public Builder setContentRecordingSession(@Nullable ContentRecordingSession session) { + mContentRecordingSession = session; + return this; + } + /** * Sets the display categories. * @@ -435,6 +469,7 @@ public final class VirtualDisplayConfig implements Parcelable { mUniqueId, mDisplayIdToMirror, mWindowManagerMirroringEnabled, + mContentRecordingSession, mDisplayCategories, mRequestedRefreshRate); } diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index d70e8b36afdb5..178a6d97dff82 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -191,20 +191,13 @@ public final class MediaProjection { } else { session = ContentRecordingSession.createTaskSession(launchCookie); } + // Pass in the current session details, so they are guaranteed to only be set in WMS + // AFTER a VirtualDisplay is constructed (assuming there are no errors during set-up). + virtualDisplayConfig.setContentRecordingSession(session); virtualDisplayConfig.setWindowManagerMirroringEnabled(true); final DisplayManager dm = mContext.getSystemService(DisplayManager.class); final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this, virtualDisplayConfig.build(), callback, handler, windowContext); - if (virtualDisplay == null) { - // Since WM handling a new display and DM creating a new VirtualDisplay is async, - // WM may have tried to start task recording and encountered an error that required - // stopping recording entirely. The VirtualDisplay would then be null when the - // MediaProjection is no longer active. - return null; - } - session.setDisplayId(virtualDisplay.getDisplay().getDisplayId()); - // Successfully set up, so save the current session details. - getProjectionService().setContentRecordingSession(session, mImpl); return virtualDisplay; } catch (RemoteException e) { // Can not capture if WMS is not accessible, so bail out. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 55d2921b2878b..ea157c89f675b 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -128,6 +128,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.Spline; +import android.view.ContentRecordingSession; import android.view.Display; import android.view.DisplayEventReceiver; import android.view.DisplayInfo; @@ -250,6 +251,7 @@ public final class DisplayManagerService extends SystemService { private ActivityManagerInternal mActivityManagerInternal; private ActivityManager mActivityManager; private UidImportanceListener mUidImportanceListener = new UidImportanceListener(); + @Nullable private IMediaProjectionManager mProjectionService; private DeviceStateManagerInternal mDeviceStateManager; @GuardedBy("mSyncRoot") @@ -1494,8 +1496,9 @@ public final class DisplayManagerService extends SystemService { final long token = Binder.clearCallingIdentity(); try { + final int displayId; synchronized (mSyncRoot) { - final int displayId = + displayId = createVirtualDisplayLocked( callback, projection, @@ -1509,8 +1512,39 @@ public final class DisplayManagerService extends SystemService { mDisplayWindowPolicyControllers.put( displayId, Pair.create(virtualDevice, dwpc)); } - return displayId; } + + // When calling setContentRecordingSession into the WindowManagerService, the WMS + // 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) { + // Only attempt to set content recording session if there are details to set and a + // VirtualDisplay has been successfully constructed. + session.setDisplayId(displayId); + + // 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. + if (!mWindowManagerInternal.setContentRecordingSession(session)) { + // Unable to start mirroring, so tear down projection & release VirtualDisplay. + try { + getProjectionService().stopActiveProjection(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to tell MediaProjectionManagerService to stop the " + + "active projection", e); + } + releaseVirtualDisplayInternal(callback.asBinder()); + return Display.INVALID_DISPLAY; + } + } + + return displayId; } finally { Binder.restoreCallingIdentity(token); } @@ -2804,8 +2838,7 @@ public final class DisplayManagerService extends SystemService { private IMediaProjectionManager getProjectionService() { if (mProjectionService == null) { - IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE); - mProjectionService = IMediaProjectionManager.Stub.asInterface(b); + mProjectionService = mInjector.getProjectionService(); } return mProjectionService; } @@ -2964,6 +2997,11 @@ public final class DisplayManagerService extends SystemService { boolean getHdrOutputConversionSupport() { return DisplayControl.getHdrOutputConversionSupport(); } + + IMediaProjectionManager getProjectionService() { + IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE); + return IMediaProjectionManager.Stub.asInterface(b); + } } @VisibleForTesting diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index eda15ae32c8b0..4f7a2ba585706 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -88,7 +88,17 @@ public class VirtualDisplayAdapter extends DisplayAdapter { // Called with SyncRoot lock held. public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { - this(syncRoot, context, handler, listener, DisplayControl::createDisplay); + this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() { + @Override + public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) { + return DisplayControl.createDisplay(name, secure, requestedRefreshRate); + } + + @Override + public void destroyDisplay(IBinder displayToken) { + DisplayControl.destroyDisplay(displayToken); + } + }); } @VisibleForTesting @@ -311,7 +321,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mSurface.release(); mSurface = null; } - DisplayControl.destroyDisplay(getDisplayTokenLocked()); + mSurfaceControlDisplayFactory.destroyDisplay(getDisplayTokenLocked()); if (mProjection != null && mMediaProjectionCallback != null) { try { mProjection.unregisterCallback(mMediaProjectionCallback); @@ -653,5 +663,12 @@ public class VirtualDisplayAdapter extends DisplayAdapter { * @return The token reference for the display in SurfaceFlinger. */ IBinder createDisplay(String name, boolean secure, float requestedRefreshRate); + + /** + * Destroy a display in SurfaceFlinger. + * + * @param displayToken The display token for the display to be destroyed. + */ + void destroyDisplay(IBinder displayToken); } } diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 71492656913bf..6861c2f049fb7 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -108,6 +108,7 @@ + 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 de1c2195fcdba..94d30bb4440bd 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -24,6 +24,8 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_D import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -32,8 +34,10 @@ import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -61,10 +65,13 @@ import android.hardware.display.HdrConversionMode; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; +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.view.ContentRecordingSession; import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayEventReceiver; @@ -158,25 +165,40 @@ public class DisplayManagerServiceTest { } }; - class BasicInjector extends DisplayManagerService.Injector { - @Override - VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context, - Handler handler, DisplayAdapter.Listener displayAdapterListener) { - return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener, - (String name, boolean secure, float refreshRate) -> mMockDisplayToken); - } + class BasicInjector extends DisplayManagerService.Injector { + @Override + IMediaProjectionManager getProjectionService() { + return mMockProjectionService; + } - @Override - LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context, - Handler handler, DisplayAdapter.Listener displayAdapterListener) { - return new LocalDisplayAdapter(syncRoot, context, handler, - displayAdapterListener, new LocalDisplayAdapter.Injector() { - @Override - public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() { - return mSurfaceControlProxy; - } - }); - } + @Override + VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context, + Handler handler, DisplayAdapter.Listener displayAdapterListener) { + return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener, + new VirtualDisplayAdapter.SurfaceControlDisplayFactory() { + @Override + public IBinder createDisplay(String name, boolean secure, + float requestedRefreshRate) { + return mMockDisplayToken; + } + + @Override + public void destroyDisplay(IBinder displayToken) { + } + }); + } + + @Override + LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context, + Handler handler, DisplayAdapter.Listener displayAdapterListener) { + return new LocalDisplayAdapter(syncRoot, context, handler, + displayAdapterListener, new LocalDisplayAdapter.Injector() { + @Override + public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() { + return mSurfaceControlProxy; + } + }); + } @Override int setHdrConversionMode(int conversionMode, int preferredHdrOutputType, @@ -198,6 +220,7 @@ public class DisplayManagerServiceTest { private final DisplayManagerService.Injector mBasicInjector = new BasicInjector(); + @Mock IMediaProjectionManager mMockProjectionService; @Mock IVirtualDeviceManager mIVirtualDeviceManager; @Mock InputManagerInternal mMockInputManagerInternal; @Mock VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal; @@ -285,6 +308,7 @@ public class DisplayManagerServiceTest { builder.setFlags(flags); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -410,6 +434,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -446,6 +471,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken, /* projection= */ null, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -479,6 +505,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken, /* projection= */ null, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -720,6 +747,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); final int firstDisplayId = binderService.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); // The second virtual display requests to mirror the first virtual display. final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2"; @@ -731,6 +759,7 @@ public class DisplayManagerServiceTest { final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(), mMockAppToken2 /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); // flush the handler @@ -768,6 +797,7 @@ public class DisplayManagerServiceTest { virtualDevice /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId; // Create a second virtual display. This should be added to the previously created display @@ -783,6 +813,7 @@ public class DisplayManagerServiceTest { virtualDevice /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId; assertEquals( @@ -820,6 +851,7 @@ public class DisplayManagerServiceTest { virtualDevice /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId; // Create a second virtual display. With the flag VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP, @@ -838,6 +870,7 @@ public class DisplayManagerServiceTest { virtualDevice /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId; assertNotEquals( @@ -881,6 +914,7 @@ public class DisplayManagerServiceTest { virtualDevice /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); // Check that FLAG_ALWAYS_UNLOCKED is set. assertNotEquals( @@ -906,6 +940,7 @@ public class DisplayManagerServiceTest { virtualDevice /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); // Check that FLAG_ALWAYS_UNLOCKED is set. assertNotEquals( @@ -929,6 +964,7 @@ public class DisplayManagerServiceTest { null /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); // Check that FLAG_ALWAYS_UNLOCKED is not set. assertEquals( @@ -960,6 +996,7 @@ public class DisplayManagerServiceTest { .setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); final int firstDisplayId = binderService.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); // The second virtual display requests to mirror the first virtual display. final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2"; @@ -971,6 +1008,7 @@ public class DisplayManagerServiceTest { final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(), mMockAppToken2 /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); // flush the handler @@ -985,6 +1023,54 @@ public class DisplayManagerServiceTest { Display.INVALID_DISPLAY); } + @Test + public void testCreateVirtualDisplay_setContentRecordingSessionSuccess() throws Exception { + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + when(mMockWindowManagerInternal + .setContentRecordingSession(any(ContentRecordingSession.class))) + .thenReturn(true); + + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, 600, 800, 320); + builder.setUniqueId("uniqueId --- setContentRecordingSession true"); + builder.setContentRecordingSession( + ContentRecordingSession.createDisplaySession(new Binder(""))); + + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + registerDefaultDisplays(displayManager); + displayManager.windowManagerAndInputReady(); + + DisplayManagerService.BinderService binderService = displayManager.new BinderService(); + final int displayId = binderService.createVirtualDisplay(builder.build(), + mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + + assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY); + } + + @Test + public void testCreateVirtualDisplay_setContentRecordingSessionFail() throws Exception { + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + when(mMockWindowManagerInternal + .setContentRecordingSession(any(ContentRecordingSession.class))) + .thenReturn(false); + + 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(); + + DisplayManagerService.BinderService binderService = displayManager.new BinderService(); + final int displayId = binderService.createVirtualDisplay(builder.build(), + mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + + assertThat(displayId).isEqualTo(Display.INVALID_DISPLAY); + } + /** * Tests that the virtual display is created with * {@link VirtualDisplayConfig.Builder#setSurface(Surface)} @@ -1011,6 +1097,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); final int displayId = binderService.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -1043,6 +1130,7 @@ public class DisplayManagerServiceTest { int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId); @@ -1093,7 +1181,6 @@ public class DisplayManagerServiceTest { registerDefaultDisplays(displayManager); - DisplayManagerService.BinderService bs = displayManager.new BinderService(); when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn( @@ -1111,6 +1198,7 @@ public class DisplayManagerServiceTest { int displayId = localService.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */, mock(DisplayWindowPolicyController.class), PACKAGE_NAME); + verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any()); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);