diff options
4 files changed, 315 insertions, 16 deletions
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 19069ea891a4..5eb15e09f09e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -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) { diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 70c9e23c6af8..cb97e2832854 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -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; } 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 6860abf40b56..062bde8f080b 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -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); diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java index 0b33c30fd7e8..657bda633ab5 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -369,6 +369,98 @@ public class LogicalDisplayMapperTest { } @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, DEVICE_STATE_CLOSED, @@ -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( + 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, int type, int width, int height, int flags) { + 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; |