Only allow mirroring on a VirtualDisplay with a mirroring flag or

MediaProjection instance.

ContentRecordingSession is set on VirtualDisplayConfig via a hidden
API; it is not enough to check it's presence before setting up
mirroring in WindowManagerService (by passing through the
ContentRecordingSession). We also check that the VirtualDisplay
is associated with either a MediaProjection, or has the appropriate
flag set.

Bug: 273540791
Test: atest FrameworksServicesTests:DisplayManagerServiceTest
Change-Id: I113f49265af2faea7fdac08afb55fc077feac5cc
This commit is contained in:
Naomi Musgrave
2023-03-14 17:54:33 +00:00
parent 96490c82ce
commit c5c96700af
2 changed files with 106 additions and 9 deletions

View File

@@ -1514,14 +1514,17 @@ public final class DisplayManagerService extends SystemService {
}
}
// 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 @@ public final class DisplayManagerService extends SystemService {
// 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 {

View File

@@ -34,7 +34,9 @@ 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.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.HdrConversionMode;
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 @@ public class DisplayManagerServiceTest {
}
@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 @@ public class DisplayManagerServiceTest {
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 @@ public class DisplayManagerServiceTest {
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)}