From b109bcf4a3f1f6c16281db49a8114f43f401a0f7 Mon Sep 17 00:00:00 2001 From: Piotr WilczyƄski Date: Wed, 29 Jan 2025 13:55:21 +0000 Subject: Fix adding displays to topology - display should be internal, external or overlay - if internal, it has to be the default display - if external or overlay, extended displays need to be enabled - display should be in default display group Bug: 384013689 Test: DisplayManagerServiceTest, DisplayTopologyCoordinatorTest Flag: com.android.server.display.feature.flags.display_topology Change-Id: I05250c7a3a91bb0df54c17f5fa9db2860963549b --- .../server/display/DisplayTopologyCoordinator.java | 25 ++- .../display/DisplayTopologyCoordinatorTest.kt | 170 ++++++++++++++++++++- 2 files changed, 185 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java index fbfe85cd3b78..2618cf40d113 100644 --- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java @@ -250,8 +250,29 @@ class DisplayTopologyCoordinator { } private boolean isDisplayAllowedInTopology(DisplayInfo info) { - return mIsExtendedDisplayEnabled.getAsBoolean() - && info.displayGroupId == Display.DEFAULT_DISPLAY_GROUP; + if (info.type != Display.TYPE_INTERNAL && info.type != Display.TYPE_EXTERNAL + && info.type != Display.TYPE_OVERLAY) { + Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " + + "type is not INTERNAL, EXTERNAL or OVERLAY"); + return false; + } + if (info.type == Display.TYPE_INTERNAL && info.displayId != Display.DEFAULT_DISPLAY) { + Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " + + "it is a non-default internal display"); + return false; + } + if ((info.type == Display.TYPE_EXTERNAL || info.type == Display.TYPE_OVERLAY) + && !mIsExtendedDisplayEnabled.getAsBoolean()) { + Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " + + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayEnabled"); + return false; + } + if (info.displayGroupId != Display.DEFAULT_DISPLAY_GROUP) { + Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " + + "it is not in the default display group"); + return false; + } + return true; } /** diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt index ca670488f6e3..3c134b5d5482 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt @@ -61,6 +61,7 @@ class DisplayTopologyCoordinatorTest { info.logicalWidth = i * 300 info.logicalHeight = i * 200 info.logicalDensityDpi = i * 100 + info.type = Display.TYPE_EXTERNAL return@map info } @@ -115,7 +116,98 @@ class DisplayTopologyCoordinatorTest { } @Test - fun addDisplay_extendedDisplaysDisabled() { + fun addDisplay_internal() { + displayInfos[0].displayId = Display.DEFAULT_DISPLAY + displayInfos[0].type = Display.TYPE_INTERNAL + coordinator.onDisplayAdded(displayInfos[0]) + + val widthDp = + pxToDp(displayInfos[0].logicalWidth.toFloat(), displayInfos[0].logicalDensityDpi) + val heightDp = + pxToDp(displayInfos[0].logicalHeight.toFloat(), displayInfos[0].logicalDensityDpi) + verify(mockTopology).addDisplay(displayInfos[0].displayId, widthDp, heightDp) + verify(mockTopologyChangedCallback).invoke( + android.util.Pair( + mockTopologyCopy, + mockTopologyGraph + ) + ) + } + + @Test + fun addDisplay_overlay() { + displayInfos[0].type = Display.TYPE_OVERLAY + coordinator.onDisplayAdded(displayInfos[0]) + + val widthDp = + pxToDp(displayInfos[0].logicalWidth.toFloat(), displayInfos[0].logicalDensityDpi) + val heightDp = + pxToDp(displayInfos[0].logicalHeight.toFloat(), displayInfos[0].logicalDensityDpi) + verify(mockTopology).addDisplay(displayInfos[0].displayId, widthDp, heightDp) + verify(mockTopologyChangedCallback).invoke( + android.util.Pair( + mockTopologyCopy, + mockTopologyGraph + ) + ) + } + + @Test + fun addDisplay_typeUnknown() { + displayInfos[0].type = Display.TYPE_UNKNOWN + + coordinator.onDisplayAdded(displayInfos[0]) + + verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun addDisplay_wifi() { + displayInfos[0].type = Display.TYPE_WIFI + + coordinator.onDisplayAdded(displayInfos[0]) + + verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun addDisplay_virtual() { + displayInfos[0].type = Display.TYPE_VIRTUAL + + coordinator.onDisplayAdded(displayInfos[0]) + + verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun addDisplay_internal_nonDefault() { + displayInfos[0].displayId = 2 + displayInfos[0].type = Display.TYPE_INTERNAL + + coordinator.onDisplayAdded(displayInfos[0]) + + verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun addDisplay_external_extendedDisplaysDisabled() { + whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) + + for (displayInfo in displayInfos) { + coordinator.onDisplayAdded(displayInfo) + } + + verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun addDisplay_overlay_extendedDisplaysDisabled() { + displayInfos[0].type = Display.TYPE_OVERLAY whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) for (displayInfo in displayInfos) { @@ -144,9 +236,16 @@ class DisplayTopologyCoordinatorTest { .thenReturn(true) addDisplay() - displayInfos[0].logicalDensityDpi += 10 + displayInfos[0].logicalWidth += 100 + displayInfos[0].logicalHeight += 100 coordinator.onDisplayChanged(displayInfos[0]) + val widthDp = + pxToDp(displayInfos[0].logicalWidth.toFloat(), displayInfos[0].logicalDensityDpi) + val heightDp = + pxToDp(displayInfos[0].logicalHeight.toFloat(), displayInfos[0].logicalDensityDpi) + verify(mockTopology).updateDisplay(displayInfos[0].displayId, widthDp, heightDp) + val captor = ArgumentCaptor.forClass(SparseIntArray::class.java) verify(mockTopologyCopy).getGraph(captor.capture()) val densities = captor.value @@ -180,11 +279,56 @@ class DisplayTopologyCoordinatorTest { } @Test - fun updateDisplay_extendedDisplaysDisabled() { + fun updateDisplay_typeUnknown() { + displayInfos[0].type = Display.TYPE_UNKNOWN + + coordinator.onDisplayChanged(displayInfos[0]) + + verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyCopy, never()).getGraph(any()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun updateDisplay_wifi() { + displayInfos[0].type = Display.TYPE_WIFI + + coordinator.onDisplayChanged(displayInfos[0]) + + verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyCopy, never()).getGraph(any()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun updateDisplay_virtual() { + displayInfos[0].type = Display.TYPE_VIRTUAL + + coordinator.onDisplayChanged(displayInfos[0]) + + verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyCopy, never()).getGraph(any()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun updateDisplay_internal_nonDefault() { + displayInfos[0].displayId = 2 + displayInfos[0].type = Display.TYPE_INTERNAL + + coordinator.onDisplayChanged(displayInfos[0]) + + verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyCopy, never()).getGraph(any()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + } + + @Test + fun updateDisplay_external_extendedDisplaysDisabled() { whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) for (displayInfo in displayInfos) { - coordinator.onDisplayAdded(displayInfo) + coordinator.onDisplayChanged(displayInfo) } verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) @@ -192,11 +336,23 @@ class DisplayTopologyCoordinatorTest { verify(mockTopologyChangedCallback, never()).invoke(any()) } + @Test + fun updateDisplay_overlay_extendedDisplaysDisabled() { + displayInfos[0].type = Display.TYPE_OVERLAY + whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) + + coordinator.onDisplayChanged(displayInfos[0]) + + verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) + verify(mockTopologyChangedCallback, never()).invoke(any()) + verify(mockTopologyStore, never()).restoreTopology(any()) + } + @Test fun updateDisplay_notInDefaultDisplayGroup() { displayInfos[0].displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 - coordinator.onDisplayAdded(displayInfos[0]) + coordinator.onDisplayChanged(displayInfos[0]) verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyCopy, never()).getGraph(any()) @@ -233,9 +389,7 @@ class DisplayTopologyCoordinatorTest { @Test fun removeDisplay_notChanged() { - for (displayInfo in displayInfos) { - coordinator.onDisplayRemoved(displayInfo.displayId) - } + coordinator.onDisplayRemoved(100) verify(mockTopologyChangedCallback, never()).invoke(any()) verify(mockTopologyStore, never()).restoreTopology(any()) -- cgit v1.2.3-59-g8ed1b