Merge "Assign virtual devices to a device display group."

This commit is contained in:
Phill Hayers
2022-10-21 10:34:50 +00:00
committed by Android (Google) Code Review
4 changed files with 315 additions and 16 deletions

View File

@@ -1356,11 +1356,19 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
final int displayId = createVirtualDisplayLocked(callback, projection, callingUid,
packageName, surface, flags, virtualDisplayConfig);
final int displayId =
createVirtualDisplayLocked(
callback,
projection,
callingUid,
packageName,
virtualDevice,
surface,
flags,
virtualDisplayConfig);
if (displayId != Display.INVALID_DISPLAY && virtualDevice != null && dwpc != null) {
mDisplayWindowPolicyControllers.put(displayId,
Pair.create(virtualDevice, dwpc));
mDisplayWindowPolicyControllers.put(
displayId, Pair.create(virtualDevice, dwpc));
}
return displayId;
}
@@ -1369,12 +1377,20 @@ public final class DisplayManagerService extends SystemService {
}
}
private int createVirtualDisplayLocked(IVirtualDisplayCallback callback,
IMediaProjection projection, int callingUid, String packageName, Surface surface,
int flags, VirtualDisplayConfig virtualDisplayConfig) {
private int createVirtualDisplayLocked(
IVirtualDisplayCallback callback,
IMediaProjection projection,
int callingUid,
String packageName,
IVirtualDevice virtualDevice,
Surface surface,
int flags,
VirtualDisplayConfig virtualDisplayConfig) {
if (mVirtualDisplayAdapter == null) {
Slog.w(TAG, "Rejecting request to create private virtual display "
+ "because the virtual display adapter is not available.");
Slog.w(
TAG,
"Rejecting request to create private virtual display "
+ "because the virtual display adapter is not available.");
return -1;
}
@@ -1385,6 +1401,19 @@ public final class DisplayManagerService extends SystemService {
return -1;
}
// If the display is to be added to a device display group, we need to make the
// LogicalDisplayMapper aware of the link between the new display and its associated virtual
// device before triggering DISPLAY_DEVICE_EVENT_ADDED.
if (virtualDevice != null && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) == 0) {
try {
final int virtualDeviceId = virtualDevice.getDeviceId();
mLogicalDisplayMapper.associateDisplayDeviceWithVirtualDevice(
device, virtualDeviceId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
// DisplayDevice events are handled manually for Virtual Displays.
// TODO: multi-display Fix this so that generic add/remove events are not handled in a
// different code path for virtual displays. Currently this happens so that we can
@@ -1393,8 +1422,7 @@ public final class DisplayManagerService extends SystemService {
// called on the DisplayThread (which we don't want to wait for?).
// One option would be to actually wait here on the binder thread
// to be notified when the virtual display is created (or failed).
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {

View File

@@ -28,6 +28,7 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -123,6 +124,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
/** Map of all display groups indexed by display group id. */
private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
/**
* Map of display groups which are linked to virtual devices (all displays in the group are
* linked to that device). Keyed by virtual device unique id.
*/
private final SparseIntArray mDeviceDisplayGroupIds = new SparseIntArray();
private final DisplayDeviceRepository mDisplayDeviceRepo;
private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
private final Listener mListener;
@@ -157,6 +164,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
*/
private final SparseIntArray mDisplayGroupsToUpdate = new SparseIntArray();
/**
* ArrayMap of display device unique ID to virtual device ID. Used in {@link
* #updateLogicalDisplaysLocked} to establish which Virtual Devices own which Virtual Displays.
*/
private final ArrayMap<String, Integer> mVirtualDeviceDisplayMapping = new ArrayMap<>();
private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
private Layout mCurrentLayout = null;
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
@@ -362,6 +375,19 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mDeviceStateToLayoutMap.dumpLocked(ipw);
}
/**
* Creates an association between a displayDevice and a virtual device. Any displays associated
* with this virtual device will be grouped together in a single {@link DisplayGroup} unless
* created with {@link Display.FLAG_OWN_DISPLAY_GROUP}.
*
* @param displayDevice the displayDevice to be linked
* @param virtualDeviceUniqueId the unique ID of the virtual device.
*/
void associateDisplayDeviceWithVirtualDevice(
DisplayDevice displayDevice, int virtualDeviceUniqueId) {
mVirtualDeviceDisplayMapping.put(displayDevice.getUniqueId(), virtualDeviceUniqueId);
}
void setDeviceStateLocked(int state, boolean isOverrideActive) {
Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
+ ", interactive=" + mInteractive);
@@ -556,6 +582,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
// Remove any virtual device mapping which exists for the display.
mVirtualDeviceDisplayMapping.remove(device.getUniqueId());
if (layoutDisplay.getAddress().equals(deviceInfo.address)) {
layout.removeDisplayLocked(DEFAULT_DISPLAY);
@@ -749,24 +778,44 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// We wait until we sent the EVENT_REMOVED event before actually removing the
// group.
mDisplayGroups.delete(id);
// Remove possible reference to the removed group.
int deviceIndex = mDeviceDisplayGroupIds.indexOfValue(id);
if (deviceIndex >= 0) {
mDeviceDisplayGroupIds.removeAt(deviceIndex);
}
}
}
}
private void assignDisplayGroupLocked(LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
final String primaryDisplayUniqueId = display.getPrimaryDisplayDeviceLocked().getUniqueId();
final Integer linkedDeviceUniqueId =
mVirtualDeviceDisplayMapping.get(primaryDisplayUniqueId);
// Get current display group data
int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
Integer deviceDisplayGroupId = null;
if (linkedDeviceUniqueId != null
&& mDeviceDisplayGroupIds.indexOfKey(linkedDeviceUniqueId) > 0) {
deviceDisplayGroupId = mDeviceDisplayGroupIds.get(linkedDeviceUniqueId);
}
final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
// Get the new display group if a change is needed
final DisplayInfo info = display.getDisplayInfoLocked();
final boolean needsOwnDisplayGroup = (info.flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0;
final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
final boolean needsDeviceDisplayGroup =
!needsOwnDisplayGroup && linkedDeviceUniqueId != null;
final boolean hasDeviceDisplayGroup =
deviceDisplayGroupId != null && groupId == deviceDisplayGroupId;
if (groupId == Display.INVALID_DISPLAY_GROUP
|| hasOwnDisplayGroup != needsOwnDisplayGroup) {
groupId = assignDisplayGroupIdLocked(needsOwnDisplayGroup);
|| hasOwnDisplayGroup != needsOwnDisplayGroup
|| hasDeviceDisplayGroup != needsDeviceDisplayGroup) {
groupId =
assignDisplayGroupIdLocked(
needsOwnDisplayGroup, needsDeviceDisplayGroup, linkedDeviceUniqueId);
}
// Create a new group if needed
@@ -931,7 +980,17 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
display.setPhase(phase);
}
private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup) {
private int assignDisplayGroupIdLocked(
boolean isOwnDisplayGroup, boolean isDeviceDisplayGroup, Integer linkedDeviceUniqueId) {
if (isDeviceDisplayGroup && linkedDeviceUniqueId != null) {
int deviceDisplayGroupId = mDeviceDisplayGroupIds.get(linkedDeviceUniqueId);
// A value of 0 indicates that no device display group was found.
if (deviceDisplayGroupId == 0) {
deviceDisplayGroupId = mNextNonDefaultGroupId++;
mDeviceDisplayGroupIds.put(linkedDeviceUniqueId, deviceDisplayGroupId);
}
return deviceDisplayGroupId;
}
return isOwnDisplayGroup ? mNextNonDefaultGroupId++ : Display.DEFAULT_DISPLAY_GROUP;
}

View File

@@ -18,6 +18,7 @@ package com.android.server.display;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
@@ -660,6 +661,117 @@ public class DisplayManagerServiceTest {
firstDisplayId);
}
/** Tests that the virtual device is created in a device display group. */
@Test
public void createVirtualDisplay_addsDisplaysToDeviceDisplayGroups() throws Exception {
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
registerDefaultDisplays(displayManager);
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY))
.thenReturn(PackageManager.PERMISSION_DENIED);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice))
.thenReturn(true);
when(virtualDevice.getDeviceId()).thenReturn(1);
// Create a first virtual display. A display group should be created for this display on the
// virtual device.
final VirtualDisplayConfig.Builder builder1 =
new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
.setUniqueId("uniqueId --- device display group 1");
int displayId1 =
localService.createVirtualDisplay(
builder1.build(),
mMockAppToken /* callback */,
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
// Create a second virtual display. This should be added to the previously created display
// group.
final VirtualDisplayConfig.Builder builder2 =
new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
.setUniqueId("uniqueId --- device display group 1");
int displayId2 =
localService.createVirtualDisplay(
builder2.build(),
mMockAppToken /* callback */,
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId;
assertEquals(
"Both displays should be added to the same displayGroup.",
displayGroupId1,
displayGroupId2);
}
/**
* Tests that the virtual display is not added to the device display group when
* VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is set.
*/
@Test
public void createVirtualDisplay_addsDisplaysToOwnDisplayGroups() throws Exception {
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
registerDefaultDisplays(displayManager);
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY))
.thenReturn(PackageManager.PERMISSION_DENIED);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice))
.thenReturn(true);
when(virtualDevice.getDeviceId()).thenReturn(1);
// Create a first virtual display. A display group should be created for this display on the
// virtual device.
final VirtualDisplayConfig.Builder builder1 =
new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
.setUniqueId("uniqueId --- device display group 1");
int displayId1 =
localService.createVirtualDisplay(
builder1.build(),
mMockAppToken /* callback */,
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
// Create a second virtual display. With the flag VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP,
// the display should not be added to the previously created display group.
final VirtualDisplayConfig.Builder builder2 =
new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
.setFlags(VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP)
.setUniqueId("uniqueId --- device display group 1");
int displayId2 =
localService.createVirtualDisplay(
builder2.build(),
mMockAppToken /* callback */,
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId;
assertNotEquals(
"Display 1 should be in the device display group and display 2 in its own display"
+ " group.",
displayGroupId1,
displayGroupId2);
}
@Test
public void testGetDisplayIdToMirror() throws Exception {
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);

View File

@@ -368,6 +368,98 @@ public class LogicalDisplayMapperTest {
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
@Test
public void testDevicesAreAddedToDeviceDisplayGroups() {
// Create the default internal display of the device.
LogicalDisplay defaultDisplay =
add(
createDisplayDevice(
Display.TYPE_INTERNAL,
600,
800,
DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY));
// Create 3 virtual displays associated with a first virtual device.
int deviceId1 = 1;
TestDisplayDevice display1 =
createDisplayDevice(Display.TYPE_VIRTUAL, "virtualDevice1Display1", 600, 800, 0);
mLogicalDisplayMapper.associateDisplayDeviceWithVirtualDevice(display1, deviceId1);
LogicalDisplay virtualDevice1Display1 = add(display1);
TestDisplayDevice display2 =
createDisplayDevice(Display.TYPE_VIRTUAL, "virtualDevice1Display2", 600, 800, 0);
mLogicalDisplayMapper.associateDisplayDeviceWithVirtualDevice(display2, deviceId1);
LogicalDisplay virtualDevice1Display2 = add(display2);
TestDisplayDevice display3 =
createDisplayDevice(Display.TYPE_VIRTUAL, "virtualDevice1Display3", 600, 800, 0);
mLogicalDisplayMapper.associateDisplayDeviceWithVirtualDevice(display3, deviceId1);
LogicalDisplay virtualDevice1Display3 = add(display3);
// Create another 3 virtual displays associated with a second virtual device.
int deviceId2 = 2;
TestDisplayDevice display4 =
createDisplayDevice(Display.TYPE_VIRTUAL, "virtualDevice2Display1", 600, 800, 0);
mLogicalDisplayMapper.associateDisplayDeviceWithVirtualDevice(display4, deviceId2);
LogicalDisplay virtualDevice2Display1 = add(display4);
TestDisplayDevice display5 =
createDisplayDevice(Display.TYPE_VIRTUAL, "virtualDevice2Display2", 600, 800, 0);
mLogicalDisplayMapper.associateDisplayDeviceWithVirtualDevice(display5, deviceId2);
LogicalDisplay virtualDevice2Display2 = add(display5);
// The final display is created with FLAG_OWN_DISPLAY_GROUP set.
TestDisplayDevice display6 =
createDisplayDevice(
Display.TYPE_VIRTUAL,
"virtualDevice2Display3",
600,
800,
DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
mLogicalDisplayMapper.associateDisplayDeviceWithVirtualDevice(display6, deviceId2);
LogicalDisplay virtualDevice2Display3 = add(display6);
// Verify that the internal display is in the default display group.
assertEquals(
DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(defaultDisplay)));
// Verify that all the displays for virtual device 1 are in the same (non-default) display
// group.
int virtualDevice1DisplayGroupId =
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(
id(virtualDevice1Display1));
assertNotEquals(DEFAULT_DISPLAY_GROUP, virtualDevice1DisplayGroupId);
assertEquals(
virtualDevice1DisplayGroupId,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(
id(virtualDevice1Display2)));
assertEquals(
virtualDevice1DisplayGroupId,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(
id(virtualDevice1Display3)));
// The first 2 displays for virtual device 2 should be in the same non-default group.
int virtualDevice2DisplayGroupId =
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(
id(virtualDevice2Display1));
assertNotEquals(DEFAULT_DISPLAY_GROUP, virtualDevice2DisplayGroupId);
assertEquals(
virtualDevice2DisplayGroupId,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(
id(virtualDevice2Display2)));
// virtualDevice2Display3 was created with FLAG_OWN_DISPLAY_GROUP and shouldn't be grouped
// with other displays of this device or be in the default display group.
assertNotEquals(
virtualDevice2DisplayGroupId,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(
id(virtualDevice2Display3)));
assertNotEquals(
DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(
id(virtualDevice2Display3)));
}
@Test
public void testDeviceShouldBeWoken() {
assertTrue(mLogicalDisplayMapper.shouldDeviceBeWoken(DEVICE_STATE_OPEN,
@@ -416,14 +508,22 @@ public class LogicalDisplayMapperTest {
/////////////////
private TestDisplayDevice createDisplayDevice(int type, int width, int height, int flags) {
return createDisplayDevice(new TestUtils.TestDisplayAddress(), type, width, height, flags);
return createDisplayDevice(
new TestUtils.TestDisplayAddress(), /* uniqueId */ "", type, width, height, flags);
}
private TestDisplayDevice createDisplayDevice(
DisplayAddress address, int type, int width, int height, int flags) {
int type, String uniqueId, int width, int height, int flags) {
return createDisplayDevice(
new TestUtils.TestDisplayAddress(), uniqueId, type, width, height, flags);
}
private TestDisplayDevice createDisplayDevice(
DisplayAddress address, String uniqueId, int type, int width, int height, int flags) {
TestDisplayDevice device = new TestDisplayDevice();
DisplayDeviceInfo displayDeviceInfo = device.getSourceInfo();
displayDeviceInfo.type = type;
displayDeviceInfo.uniqueId = uniqueId;
displayDeviceInfo.width = width;
displayDeviceInfo.height = height;
displayDeviceInfo.flags = flags;