summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml8
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java11
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java80
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java10
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java26
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java253
8 files changed, 366 insertions, 33 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b90ee2b7e57f..8a2d767e9ad1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6918,6 +6918,14 @@
entering the corresponding modes -->
<string name="config_rearDisplayPhysicalAddress" translatable="false"></string>
+ <!-- The maximum number of virtual displays that can exist at the same time.
+ It must be >= 1. -->
+ <integer name="config_virtualDisplayLimit">100</integer>
+
+ <!-- The maximum number of virtual displays per package that can exist at the same time.
+ It must be >= 1. -->
+ <integer name="config_virtualDisplayLimitPerPackage">50</integer>
+
<!-- List of certificate to be used for font fs-verity integrity verification -->
<string-array translatable="false" name="config_fontManagerServiceCerts">
</string-array>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b7c876545f05..c1893ab85d2b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5240,6 +5240,9 @@
<java-symbol type="integer" name="config_deviceStateConcurrentRearDisplay" />
<java-symbol type="string" name="config_rearDisplayPhysicalAddress" />
+ <java-symbol type="integer" name="config_virtualDisplayLimit" />
+ <java-symbol type="integer" name="config_virtualDisplayLimitPerPackage" />
+
<!-- For app language picker -->
<java-symbol type="string" name="system_locale_title" />
<java-symbol type="layout" name="app_language_picker_system_default" />
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d71826ffc273..179ec63458dd 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1978,7 +1978,7 @@ public final class DisplayManagerService extends SystemService {
// handles stopping the projection.
Slog.w(TAG, "Content Recording: failed to start mirroring - "
+ "releasing virtual display " + displayId);
- releaseVirtualDisplayInternal(callback.asBinder());
+ releaseVirtualDisplayInternal(callback.asBinder(), callingUid);
return Display.INVALID_DISPLAY;
} else if (projection != null) {
// Indicate that this projection has been used to record, and can't be used
@@ -2067,7 +2067,7 @@ public final class DisplayManagerService extends SystemService {
// Something weird happened and the logical display was not created.
Slog.w(TAG, "Rejecting request to create virtual display "
+ "because the logical display was not created.");
- mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder(), callingUid);
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
return -1;
@@ -2094,14 +2094,14 @@ public final class DisplayManagerService extends SystemService {
}
}
- private void releaseVirtualDisplayInternal(IBinder appToken) {
+ private void releaseVirtualDisplayInternal(IBinder appToken, int callingUid) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
return;
}
DisplayDevice device =
- mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken, callingUid);
Slog.d(TAG, "Virtual Display: Display Device released");
if (device != null) {
// TODO: multi-display - handle virtual displays the same as other display adapters.
@@ -4620,9 +4620,10 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public void releaseVirtualDisplay(IVirtualDisplayCallback callback) {
+ final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- releaseVirtualDisplayInternal(callback.asBinder());
+ releaseVirtualDisplayInternal(callback.asBinder(), callingUid);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index e77c5ec50f8b..421145390190 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -55,12 +55,14 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayShape;
import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -85,6 +87,11 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private static final AtomicInteger sNextUniqueIndex = new AtomicInteger(0);
private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>();
+
+ private final int mMaxDevices;
+ private final int mMaxDevicesPerPackage;
+ private final SparseIntArray mNoOfDevicesPerPackage = new SparseIntArray();
+
private final Handler mHandler;
private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
@@ -114,8 +121,31 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
super(syncRoot, context, handler, listener, TAG, featureFlags);
mHandler = handler;
mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
+
+ mMaxDevices = context.getResources().getInteger(R.integer.config_virtualDisplayLimit);
+ if (mMaxDevices < 1) {
+ throw new IllegalArgumentException("The limit of virtual displays must be >= 1");
+ }
+ mMaxDevicesPerPackage =
+ context.getResources().getInteger(R.integer.config_virtualDisplayLimitPerPackage);
+ if (mMaxDevicesPerPackage < 1) {
+ throw new IllegalArgumentException(
+ "The limit of virtual displays per package must be >= 1");
+ }
}
+ /**
+ * Create a virtual display
+ * @param callback The callback
+ * @param projection The media projection
+ * @param ownerUid The UID of the package creating a display
+ * @param ownerPackageName The name of the package creating a display
+ * @param uniqueId The unique ID of the display device
+ * @param surface The surface
+ * @param flags The flags
+ * @param virtualDisplayConfig The config
+ * @return The display device created
+ */
public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId,
Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) {
@@ -126,6 +156,22 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
return null;
}
+ if (getFeatureFlags().isVirtualDisplayLimitEnabled()
+ && mVirtualDisplayDevices.size() >= mMaxDevices) {
+ Slog.w(TAG, "Rejecting request to create private virtual display because "
+ + mMaxDevices + " devices already exist.");
+ return null;
+ }
+
+ int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
+ if (getFeatureFlags().isVirtualDisplayLimitEnabled()
+ && noOfDevices >= mMaxDevicesPerPackage) {
+ Slog.w(TAG, "Rejecting request to create private virtual display because "
+ + mMaxDevicesPerPackage + " devices already exist for package "
+ + ownerPackageName + ".");
+ return null;
+ }
+
String name = virtualDisplayConfig.getName();
boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
@@ -140,6 +186,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig);
mVirtualDisplayDevices.put(appToken, device);
+ if (getFeatureFlags().isVirtualDisplayLimitEnabled()) {
+ mNoOfDevicesPerPackage.put(ownerUid, noOfDevices + 1);
+ }
try {
if (projection != null) {
@@ -150,7 +199,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
appToken.linkToDeath(device, 0);
} catch (RemoteException ex) {
Slog.e(TAG, "Virtual Display: error while setting up VirtualDisplayDevice", ex);
- mVirtualDisplayDevices.remove(appToken);
+ removeVirtualDisplayDeviceLocked(appToken, ownerUid);
device.destroyLocked(false);
return null;
}
@@ -194,8 +243,15 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
}
- public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
- VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
+ /**
+ * Release a virtual display that was previously created
+ * @param appToken The token to identify the display
+ * @param ownerUid The UID of the package, used to keep track of and limit the number of
+ * displays created per package
+ * @return The display device that has been removed
+ */
+ public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken, int ownerUid) {
+ VirtualDisplayDevice device = removeVirtualDisplayDeviceLocked(appToken, ownerUid);
if (device != null) {
Slog.v(TAG, "Release VirtualDisplay " + device.mName);
device.destroyLocked(true);
@@ -228,10 +284,6 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
: ("," + uid + "," + config.getName() + "," + sNextUniqueIndex.getAndIncrement()));
}
- private void handleBinderDiedLocked(IBinder appToken) {
- mVirtualDisplayDevices.remove(appToken);
- }
-
private void handleMediaProjectionStoppedLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
if (device != null) {
@@ -241,6 +293,18 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
}
+ private VirtualDisplayDevice removeVirtualDisplayDeviceLocked(IBinder appToken, int ownerUid) {
+ int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
+ if (getFeatureFlags().isVirtualDisplayLimitEnabled()) {
+ if (noOfDevices <= 1) {
+ mNoOfDevicesPerPackage.delete(ownerUid);
+ } else {
+ mNoOfDevicesPerPackage.put(ownerUid, noOfDevices - 1);
+ }
+ }
+ return mVirtualDisplayDevices.remove(appToken);
+ }
+
private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
private static final int PENDING_SURFACE_CHANGE = 0x01;
private static final int PENDING_RESIZE = 0x02;
@@ -300,7 +364,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
@Override
public void binderDied() {
synchronized (getSyncRoot()) {
- handleBinderDiedLocked(mAppToken);
+ removeVirtualDisplayDeviceLocked(mAppToken, mOwnerUid);
Slog.i(TAG, "Virtual display device released because application token died: "
+ mOwnerPackageName);
destroyLocked(false);
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 5284d1c423f6..eeec36919480 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -199,6 +199,11 @@ public class DisplayManagerFlags {
Flags.FLAG_IDLE_SCREEN_CONFIG_IN_SUBSCRIBING_LIGHT_SENSOR,
Flags::idleScreenConfigInSubscribingLightSensor);
+ private final FlagState mVirtualDisplayLimit =
+ new FlagState(
+ Flags.FLAG_VIRTUAL_DISPLAY_LIMIT,
+ Flags::virtualDisplayLimit);
+
private final FlagState mNormalBrightnessForDozeParameter = new FlagState(
Flags.FLAG_NORMAL_BRIGHTNESS_FOR_DOZE_PARAMETER,
Flags::normalBrightnessForDozeParameter
@@ -416,6 +421,10 @@ public class DisplayManagerFlags {
return mNewHdrBrightnessModifier.isEnabled();
}
+ public boolean isVirtualDisplayLimitEnabled() {
+ return mVirtualDisplayLimit.isEnabled();
+ }
+
/**
* @return Whether the useDozeBrightness parameter should be used
*/
@@ -487,6 +496,7 @@ public class DisplayManagerFlags {
pw.println(" " + mOffloadDozeOverrideHoldsWakelock);
pw.println(" " + mOffloadSessionCancelBlockScreenOn);
pw.println(" " + mNewHdrBrightnessModifier);
+ pw.println(" " + mVirtualDisplayLimit);
pw.println(" " + mNormalBrightnessForDozeParameter);
pw.println(" " + mIdleScreenConfigInSubscribingLightSensor);
pw.println(" " + mEnableBatteryStatsForAllDisplays);
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 252ed09fd125..20ecd2d8303e 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -348,6 +348,14 @@ flag {
}
flag {
+ name: "virtual_display_limit"
+ namespace: "display_manager"
+ description: "Limit the number of virtual displays that can be created."
+ bug: "261791612"
+ is_fixed_read_only: true
+}
+
+flag {
name: "idle_screen_config_in_subscribing_light_sensor"
namespace: "display_manager"
description: "Account for Idle screen refresh rate configs while subscribing to light sensor"
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 6093a67cd320..1a1c8e50ba0a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -50,6 +50,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
@@ -3445,6 +3446,31 @@ public class DisplayManagerServiceTest {
verify(dpc).onDisplayChanged(/* hbmMetadata= */ null, Layout.NO_LEAD_DISPLAY);
}
+ @Test
+ public void testCreateAndReleaseVirtualDisplay_CalledWithTheSameUid() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ registerDefaultDisplays(displayManager);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ VirtualDisplayConfig config = mock(VirtualDisplayConfig.class);
+ Surface surface = mock(Surface.class);
+ when(config.getSurface()).thenReturn(surface);
+ int callingUid = Binder.getCallingUid();
+ IBinder binder = mock(IBinder.class);
+ when(mMockAppToken.asBinder()).thenReturn(binder);
+ String uniqueId = "123";
+ when(config.getUniqueId()).thenReturn(uniqueId);
+
+ bs.createVirtualDisplay(config, mMockAppToken, /* projection= */ null, PACKAGE_NAME);
+ verify(mMockVirtualDisplayAdapter).createVirtualDisplayLocked(eq(mMockAppToken),
+ /* projection= */ isNull(), eq(callingUid), eq(PACKAGE_NAME),
+ eq("virtual:" + PACKAGE_NAME + ":" + uniqueId), eq(surface), /* flags= */ anyInt(),
+ eq(config));
+
+ bs.releaseVirtualDisplay(mMockAppToken);
+ verify(mMockVirtualDisplayAdapter).releaseVirtualDisplayLocked(binder, callingUid);
+ }
+
private void initDisplayPowerController(DisplayManagerInternal localService) {
localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 81e6cc3f546b..3ac7fb0dbe53 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -21,71 +21,104 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
+import android.media.projection.IMediaProjection;
import android.os.IBinder;
import android.os.Process;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.testing.TestableContext;
+import android.view.Surface;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.TestHandler;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class VirtualDisplayAdapterTest {
+ private static final int MAX_DEVICES = 3;
+ private static final int MAX_DEVICES_PER_PACKAGE = 2;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
@Mock
- Context mContextMock;
+ private VirtualDisplayAdapter.SurfaceControlDisplayFactory mMockSufaceControlDisplayFactory;
@Mock
- VirtualDisplayAdapter.SurfaceControlDisplayFactory mMockSufaceControlDisplayFactory;
+ private DisplayAdapter.Listener mMockListener;
@Mock
- DisplayAdapter.Listener mMockListener;
+ private IVirtualDisplayCallback mMockCallback;
@Mock
- IVirtualDisplayCallback mMockCallback;
+ private IBinder mMockBinder;
@Mock
- IBinder mMockBinder;
+ private IMediaProjection mMediaProjectionMock;
- private TestHandler mHandler;
+ @Mock
+ private Surface mSurfaceMock;
+
+ @Mock
+ private VirtualDisplayConfig mVirtualDisplayConfigMock;
- private VirtualDisplayAdapter mVirtualDisplayAdapter;
+ private TestHandler mHandler;
@Mock
private DisplayManagerFlags mFeatureFlags;
+ private VirtualDisplayAdapter mAdapter;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ mContext.getOrCreateTestableResources().addOverride(R.integer.config_virtualDisplayLimit,
+ MAX_DEVICES);
+ mContext.getOrCreateTestableResources().addOverride(
+ R.integer.config_virtualDisplayLimitPerPackage, MAX_DEVICES_PER_PACKAGE);
+
mHandler = new TestHandler(null);
- mVirtualDisplayAdapter = new VirtualDisplayAdapter(new DisplayManagerService.SyncRoot(),
- mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory,
- mFeatureFlags);
+ mAdapter = new VirtualDisplayAdapter(new DisplayManagerService.SyncRoot(), mContext,
+ mHandler, mMockListener, mMockSufaceControlDisplayFactory, mFeatureFlags);
when(mMockCallback.asBinder()).thenReturn(mMockBinder);
}
@Test
- public void testCreatesVirtualDisplay() {
+ public void testCreateAndReleaseVirtualDisplay() {
VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("test", /* width= */ 1,
/* height= */ 1, /* densityDpi= */ 1).build();
+ int ownerUid = 10;
- DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
- /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
+ DisplayDevice result = mAdapter.createVirtualDisplayLocked(mMockCallback,
+ /* projection= */ null, ownerUid, /* packageName= */ "testpackage",
/* uniqueId= */ "uniqueId", /* surface= */ null, /* flags= */ 0, config);
+ assertNotNull(result);
+ result = mAdapter.releaseVirtualDisplayLocked(mMockBinder, ownerUid);
assertNotNull(result);
}
@@ -98,7 +131,7 @@ public class VirtualDisplayAdapterTest {
final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
packageName, Process.myUid(), config);
- DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(
+ DisplayDevice result = mAdapter.createVirtualDisplayLocked(
mMockCallback, /* projection= */ null, /* ownerUid= */ 10,
packageName, displayUniqueId, /* surface= */ null, /* flags= */ 0, config);
@@ -114,14 +147,194 @@ public class VirtualDisplayAdapterTest {
/* height= */ 1, /* densityDpi= */ 1).build();
VirtualDisplayConfig config2 = new VirtualDisplayConfig.Builder("test2", /* width= */ 1,
/* height= */ 1, /* densityDpi= */ 1).build();
- mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null,
- /* ownerUid= */ 10, /* packageName= */ "testpackage", /* uniqueId= */ "uniqueId1",
- /* surface= */ null, /* flags= */ 0, config1);
- DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
+ DisplayDevice result = mAdapter.createVirtualDisplayLocked(mMockCallback,
/* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
- /* uniqueId= */ "uniqueId2", /* surface= */ null, /* flags= */ 0, config2);
+ /* uniqueId= */ "uniqueId1", /* surface= */ null, /* flags= */ 0, config1);
+ assertNotNull(result);
+ result = mAdapter.createVirtualDisplayLocked(mMockCallback,
+ /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
+ /* uniqueId= */ "uniqueId2", /* surface= */ null, /* flags= */ 0, config2);
assertNull(result);
}
+
+ @Test
+ public void testCreateManyVirtualDisplays_LimitFlagDisabled() {
+ when(mFeatureFlags.isVirtualDisplayLimitEnabled()).thenReturn(false);
+
+ // Displays for the same package
+ for (int i = 0; i < MAX_DEVICES_PER_PACKAGE * 2; i++) {
+ // Same owner UID
+ IVirtualDisplayCallback callback = createCallback();
+ DisplayDevice device = mAdapter.createVirtualDisplayLocked(callback,
+ mMediaProjectionMock, 1234, "test.package", "123",
+ mSurfaceMock, /* flags= */ 0, mVirtualDisplayConfigMock);
+ assertNotNull(device);
+ }
+
+ // Displays for different packages
+ for (int i = 0; i < MAX_DEVICES * 2; i++) {
+ // Same owner UID
+ IVirtualDisplayCallback callback = createCallback();
+ DisplayDevice device = mAdapter.createVirtualDisplayLocked(callback,
+ mMediaProjectionMock, 1234 + i, "test.package", "123",
+ mSurfaceMock, /* flags= */ 0, mVirtualDisplayConfigMock);
+ assertNotNull(device);
+ }
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_MaxDisplaysPerPackage() {
+ when(mFeatureFlags.isVirtualDisplayLimitEnabled()).thenReturn(true);
+ List<IVirtualDisplayCallback> callbacks = new ArrayList<>();
+ int ownerUid = 1234;
+
+ for (int i = 0; i < MAX_DEVICES_PER_PACKAGE * 2; i++) {
+ // Same owner UID
+ IVirtualDisplayCallback callback = createCallback();
+ DisplayDevice device = mAdapter.createVirtualDisplayLocked(callback,
+ mMediaProjectionMock, ownerUid, "test.package", "123",
+ mSurfaceMock, /* flags= */ 0, mVirtualDisplayConfigMock);
+ if (i < MAX_DEVICES_PER_PACKAGE) {
+ assertNotNull(device);
+ callbacks.add(callback);
+ } else {
+ assertNull(device);
+ }
+ }
+
+ // Release one display
+ DisplayDevice device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(0).asBinder(),
+ ownerUid);
+ assertNotNull(device);
+ callbacks.remove(0);
+
+ // We should be able to create another display
+ IVirtualDisplayCallback callback = createCallback();
+ device = mAdapter.createVirtualDisplayLocked(callback, mMediaProjectionMock, ownerUid,
+ "test.package", "123", mSurfaceMock, /* flags= */ 0,
+ mVirtualDisplayConfigMock);
+ assertNotNull(device);
+ callbacks.add(callback);
+
+ // But only one
+ callback = createCallback();
+ device = mAdapter.createVirtualDisplayLocked(callback, mMediaProjectionMock, ownerUid,
+ "test.package", "123", mSurfaceMock, /* flags= */ 0,
+ mVirtualDisplayConfigMock);
+ assertNull(device);
+
+ // Release all the displays
+ for (IVirtualDisplayCallback cb : callbacks) {
+ device = mAdapter.releaseVirtualDisplayLocked(cb.asBinder(), ownerUid);
+ assertNotNull(device);
+ }
+ callbacks.clear();
+
+ // We should be able to create the max number of displays again
+ for (int i = 0; i < MAX_DEVICES_PER_PACKAGE * 2; i++) {
+ // Same owner UID
+ callback = createCallback();
+ device = mAdapter.createVirtualDisplayLocked(callback, mMediaProjectionMock, ownerUid,
+ "test.package", "123", mSurfaceMock, /* flags= */ 0,
+ mVirtualDisplayConfigMock);
+ if (i < MAX_DEVICES_PER_PACKAGE) {
+ assertNotNull(device);
+ callbacks.add(callback);
+ } else {
+ assertNull(device);
+ }
+ }
+
+ // We should be able to create a display for a different package
+ callback = createCallback();
+ device = mAdapter.createVirtualDisplayLocked(callback, mMediaProjectionMock, ownerUid + 1,
+ "test.package", "123", mSurfaceMock, /* flags= */ 0,
+ mVirtualDisplayConfigMock);
+ assertNotNull(device);
+ callbacks.add(callback);
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_MaxDisplays() {
+ when(mFeatureFlags.isVirtualDisplayLimitEnabled()).thenReturn(true);
+ List<IVirtualDisplayCallback> callbacks = new ArrayList<>();
+ int firstOwnerUid = 1000;
+
+ for (int i = 0; i < MAX_DEVICES * 2; i++) {
+ // Different owner UID
+ IVirtualDisplayCallback callback = createCallback();
+ DisplayDevice device = mAdapter.createVirtualDisplayLocked(callback,
+ mMediaProjectionMock, firstOwnerUid + i, "test.package",
+ "123", mSurfaceMock, /* flags= */ 0, mVirtualDisplayConfigMock);
+ if (i < MAX_DEVICES) {
+ assertNotNull(device);
+ callbacks.add(callback);
+ } else {
+ assertNull(device);
+ }
+ }
+
+ // Release one display
+ DisplayDevice device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(0).asBinder(),
+ firstOwnerUid);
+ assertNotNull(device);
+ callbacks.remove(0);
+
+ // We should be able to create another display
+ IVirtualDisplayCallback callback = createCallback();
+ device = mAdapter.createVirtualDisplayLocked(callback, mMediaProjectionMock,
+ firstOwnerUid, "test.package", "123", mSurfaceMock, /* flags= */ 0,
+ mVirtualDisplayConfigMock);
+ assertNotNull(device);
+ callbacks.add(callback);
+
+ // But only one
+ callback = createCallback();
+ device = mAdapter.createVirtualDisplayLocked(callback, mMediaProjectionMock,
+ firstOwnerUid, "test.package", "123", mSurfaceMock, /* flags= */ 0,
+ mVirtualDisplayConfigMock);
+ assertNull(device);
+
+ // Release all the displays
+ for (int i = 0; i < callbacks.size(); i++) {
+ device = mAdapter.releaseVirtualDisplayLocked(callbacks.get(i).asBinder(),
+ firstOwnerUid + i);
+ assertNotNull(device);
+ }
+ callbacks.clear();
+
+ // We should be able to create the max number of displays again
+ for (int i = 0; i < MAX_DEVICES * 2; i++) {
+ // Different owner UID
+ callback = createCallback();
+ device = mAdapter.createVirtualDisplayLocked(callback, mMediaProjectionMock,
+ firstOwnerUid + i, "test.package", "123", mSurfaceMock,
+ /* flags= */ 0, mVirtualDisplayConfigMock);
+ if (i < MAX_DEVICES) {
+ assertNotNull(device);
+ callbacks.add(callback);
+ } else {
+ assertNull(device);
+ }
+ }
+ }
+
+ private IVirtualDisplayCallback createCallback() {
+ return new IVirtualDisplayCallback.Stub() {
+
+ @Override
+ public void onPaused() {
+ }
+
+ @Override
+ public void onResumed() {
+ }
+
+ @Override
+ public void onStopped() {
+ }
+ };
+ }
}