Loading core/java/android/hardware/display/DisplayTopology.java +1 −1 Original line number Diff line number Diff line Loading @@ -750,7 +750,7 @@ public final class DisplayTopology implements Parcelable { new SparseArray<>(); for (int id : displayIds) { if (densityPerDisplay.get(id) == 0) { Slog.w(TAG, "Cannot construct graph, no density for display " + id); Slog.e(TAG, "Cannot construct graph, no density for display " + id); return null; } adjacentDisplaysPerId.append(id, new ArrayList<>(Math.min(10, displayIds.size()))); Loading services/core/java/com/android/server/display/DisplayManagerService.java +10 −1 Original line number Diff line number Diff line Loading @@ -107,6 +107,7 @@ import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayTopologyGraph; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; Loading Loading @@ -292,6 +293,7 @@ public final class DisplayManagerService extends SystemService { private final DisplayModeDirector mDisplayModeDirector; private final ExternalDisplayPolicy mExternalDisplayPolicy; private WindowManagerInternal mWindowManagerInternal; @Nullable private InputManagerInternal mInputManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private final UidImportanceListener mUidImportanceListener = new UidImportanceListener(); Loading Loading @@ -680,8 +682,15 @@ public final class DisplayManagerService extends SystemService { mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector()); if (mFlags.isDisplayTopologyEnabled()) { final var backupManager = new BackupManager(mContext); Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback = update -> { if (mInputManagerInternal != null) { mInputManagerInternal.setDisplayTopology(update.second); } deliverTopologyUpdate(update.first); }; mDisplayTopologyCoordinator = new DisplayTopologyCoordinator( this::isExtendedDisplayEnabled, this::deliverTopologyUpdate, this::isExtendedDisplayEnabled, topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged); } else { mDisplayTopologyCoordinator = null; Loading services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +23 −5 Original line number Diff line number Diff line Loading @@ -19,8 +19,11 @@ package com.android.server.display; import static android.hardware.display.DisplayTopology.pxToDp; import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayTopologyGraph; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.Display; import android.view.DisplayInfo; Loading Loading @@ -53,6 +56,12 @@ class DisplayTopologyCoordinator { @GuardedBy("mSyncRoot") private DisplayTopology mTopology; // Map from logical display ID to logical display density. Should always be consistent with // mTopology. @GuardedBy("mSyncRoot") private final SparseIntArray mDensities = new SparseIntArray(); @GuardedBy("mSyncRoot") private final Map<String, Integer> mUniqueIdToDisplayIdMapping = new HashMap<>(); Loading @@ -69,13 +78,13 @@ class DisplayTopologyCoordinator { * Should be invoked from the corresponding executor. * A copy of the topology should be sent that will not be modified by the system. */ private final Consumer<DisplayTopology> mOnTopologyChangedCallback; private final Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> mOnTopologyChangedCallback; private final Executor mTopologyChangeExecutor; private final DisplayManagerService.SyncRoot mSyncRoot; private final Runnable mTopologySavedCallback; DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayEnabled, Consumer<DisplayTopology> onTopologyChangedCallback, Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback, Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot, Runnable topologySavedCallback) { this(new Injector(), isExtendedDisplayEnabled, onTopologyChangedCallback, Loading @@ -84,7 +93,7 @@ class DisplayTopologyCoordinator { @VisibleForTesting DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayEnabled, Consumer<DisplayTopology> onTopologyChangedCallback, Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback, Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot, Runnable topologySavedCallback) { mTopology = injector.getTopology(); Loading @@ -107,7 +116,7 @@ class DisplayTopologyCoordinator { } synchronized (mSyncRoot) { addDisplayIdMappingLocked(info); mDensities.put(info.displayId, info.logicalDensityDpi); mTopology.addDisplay(info.displayId, getWidth(info), getHeight(info)); restoreTopologyLocked(); sendTopologyUpdateLocked(); Loading @@ -119,7 +128,13 @@ class DisplayTopologyCoordinator { * @param info The new display info */ void onDisplayChanged(DisplayInfo info) { if (!isDisplayAllowedInTopology(info)) { return; } synchronized (mSyncRoot) { if (mDensities.indexOfKey(info.displayId) >= 0) { mDensities.put(info.displayId, info.logicalDensityDpi); } if (mTopology.updateDisplay(info.displayId, getWidth(info), getHeight(info))) { sendTopologyUpdateLocked(); } Loading @@ -132,6 +147,7 @@ class DisplayTopologyCoordinator { */ void onDisplayRemoved(int displayId) { synchronized (mSyncRoot) { mDensities.delete(displayId); if (mTopology.removeDisplay(displayId)) { removeDisplayIdMappingLocked(displayId); restoreTopologyLocked(); Loading Loading @@ -256,7 +272,9 @@ class DisplayTopologyCoordinator { @GuardedBy("mSyncRoot") private void sendTopologyUpdateLocked() { DisplayTopology copy = mTopology.copy(); mTopologyChangeExecutor.execute(() -> mOnTopologyChangedCallback.accept(copy)); SparseIntArray densities = mDensities.clone(); mTopologyChangeExecutor.execute(() -> mOnTopologyChangedCallback.accept( new Pair<>(copy, copy.getGraph(densities)))); } @VisibleForTesting Loading services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +11 −1 Original line number Diff line number Diff line Loading @@ -107,6 +107,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayOffloader; import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayTopologyGraph; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; Loading Loading @@ -136,6 +137,7 @@ import android.provider.Settings.SettingNotFoundException; import android.test.mock.MockContentResolver; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.Spline; import android.view.ContentRecordingSession; import android.view.Display; Loading Loading @@ -3875,8 +3877,16 @@ public class DisplayManagerServiceTest { displayManager.new BinderService(); registerDefaultDisplays(displayManager); initDisplayPowerController(localService); displayManager.windowManagerAndInputReady(); DisplayTopology topology = mock(DisplayTopology.class); when(topology.copy()).thenReturn(topology); DisplayTopologyGraph graph = mock(DisplayTopologyGraph.class); when(topology.getGraph(any(SparseIntArray.class))).thenReturn(graph); displayManagerBinderService.setDisplayTopology(topology); waitForIdleHandler(displayManager.getDisplayHandler()); displayManagerBinderService.setDisplayTopology(new DisplayTopology()); verify(mMockInputManagerInternal).setDisplayTopology(graph); } @Test Loading services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt +138 −29 Original line number Diff line number Diff line Loading @@ -18,39 +18,51 @@ package com.android.server.display import android.hardware.display.DisplayTopology import android.hardware.display.DisplayTopology.pxToDp import android.hardware.display.DisplayTopologyGraph import android.util.SparseArray import android.util.SparseIntArray import android.view.Display import android.view.DisplayInfo import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.any import org.mockito.kotlin.clearInvocations import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever class DisplayTopologyCoordinatorTest { private lateinit var coordinator: DisplayTopologyCoordinator private val displayInfo = DisplayInfo() private lateinit var displayInfos: List<DisplayInfo> private val topologyChangeExecutor = Runnable::run private val mockTopologyStore = mock<DisplayTopologyStore>() private val mockTopology = mock<DisplayTopology>() private val mockTopologyCopy = mock<DisplayTopology>() private val mockTopologyGraph = mock<DisplayTopologyGraph>() private val mockIsExtendedDisplayEnabled = mock<() -> Boolean>() private val mockTopologySavedCallback = mock<() -> Unit>() private val mockTopologyChangedCallback = mock<(DisplayTopology) -> Unit>() private val mockTopologyChangedCallback = mock<(android.util.Pair<DisplayTopology, DisplayTopologyGraph>) -> Unit>() @Before fun setUp() { displayInfo.displayId = Display.DEFAULT_DISPLAY displayInfo.logicalWidth = 300 displayInfo.logicalHeight = 200 displayInfo.logicalDensityDpi = 100 displayInfos = (1..10).map { i -> val info = DisplayInfo() info.displayId = i info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP info.logicalWidth = i * 300 info.logicalHeight = i * 200 info.logicalDensityDpi = i * 100 return@map info } val injector = object : DisplayTopologyCoordinator.Injector() { override fun getTopology() = mockTopology Loading @@ -62,6 +74,7 @@ class DisplayTopologyCoordinatorTest { } whenever(mockIsExtendedDisplayEnabled()).thenReturn(true) whenever(mockTopology.copy()).thenReturn(mockTopologyCopy) whenever(mockTopologyCopy.getGraph(any())).thenReturn(mockTopologyGraph) coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayEnabled, mockTopologyChangedCallback, topologyChangeExecutor, DisplayManagerService.SyncRoot(), mockTopologySavedCallback) Loading @@ -69,20 +82,45 @@ class DisplayTopologyCoordinatorTest { @Test fun addDisplay() { displayInfos.forEachIndexed { i, displayInfo -> coordinator.onDisplayAdded(displayInfo) val widthDp = pxToDp(displayInfo.logicalWidth.toFloat(), displayInfo.logicalDensityDpi) val heightDp = pxToDp(displayInfo.logicalHeight.toFloat(), displayInfo.logicalDensityDpi) val heightDp = pxToDp(displayInfo.logicalHeight.toFloat(), displayInfo.logicalDensityDpi) verify(mockTopology).addDisplay(displayInfo.displayId, widthDp, heightDp) verify(mockTopologyChangedCallback).invoke(mockTopologyCopy) verify(mockTopologyStore).restoreTopology(mockTopology) } val captor = ArgumentCaptor.forClass(SparseIntArray::class.java) verify(mockTopologyCopy, times(displayInfos.size)).getGraph(captor.capture()) val densities = captor.value assertThat(densities.size()).isEqualTo(displayInfos.size) for (displayInfo in displayInfos) { assertThat(densities.get(displayInfo.displayId)) .isEqualTo(displayInfo.logicalDensityDpi) } verify(mockTopologyChangedCallback, times(displayInfos.size)).invoke( android.util.Pair( mockTopologyCopy, mockTopologyGraph ) ) verify(mockTopologyStore, times(displayInfos.size)).restoreTopology(mockTopology) // Clear invocations for other tests that call this method clearInvocations(mockTopologyCopy) clearInvocations(mockTopologyChangedCallback) clearInvocations(mockTopologyStore) } @Test fun addDisplay_extendedDisplaysDisabled() { whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) for (displayInfo in displayInfos) { coordinator.onDisplayAdded(displayInfo) } verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyChangedCallback, never()).invoke(any()) Loading @@ -91,9 +129,9 @@ class DisplayTopologyCoordinatorTest { @Test fun addDisplay_notInDefaultDisplayGroup() { displayInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 displayInfos[0].displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 coordinator.onDisplayAdded(displayInfo) coordinator.onDisplayAdded(displayInfos[0]) verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyChangedCallback, never()).invoke(any()) Loading @@ -102,39 +140,102 @@ class DisplayTopologyCoordinatorTest { @Test fun updateDisplay() { whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat())) whenever(mockTopology.updateDisplay(eq(displayInfos[0].displayId), anyFloat(), anyFloat())) .thenReturn(true) addDisplay() coordinator.onDisplayChanged(displayInfo) displayInfos[0].logicalDensityDpi += 10 coordinator.onDisplayChanged(displayInfos[0]) verify(mockTopologyChangedCallback).invoke(mockTopologyCopy) val captor = ArgumentCaptor.forClass(SparseIntArray::class.java) verify(mockTopologyCopy).getGraph(captor.capture()) val densities = captor.value assertThat(densities.size()).isEqualTo(displayInfos.size) assertThat(densities.get(displayInfos[0].displayId)) .isEqualTo(displayInfos[0].logicalDensityDpi) verify(mockTopologyChangedCallback).invoke( android.util.Pair( mockTopologyCopy, mockTopologyGraph ) ) } @Test fun updateDisplay_notChanged() { whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat())) .thenReturn(false) addDisplay() for (displayInfo in displayInfos) { coordinator.onDisplayChanged(displayInfo) } // Try to update a display that does not exist val info = DisplayInfo() info.displayId = 100 coordinator.onDisplayChanged(info) verify(mockTopologyCopy, never()).getGraph(any()) verify(mockTopologyChangedCallback, never()).invoke(any()) } @Test fun updateDisplay_extendedDisplaysDisabled() { whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) for (displayInfo in displayInfos) { coordinator.onDisplayAdded(displayInfo) } verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyCopy, never()).getGraph(any()) verify(mockTopologyChangedCallback, never()).invoke(any()) } @Test fun updateDisplay_notInDefaultDisplayGroup() { displayInfos[0].displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 coordinator.onDisplayAdded(displayInfos[0]) verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyCopy, never()).getGraph(any()) verify(mockTopologyChangedCallback, never()).invoke(any()) } @Test fun removeDisplay() { whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(true) addDisplay() val displaysToRemove = listOf(0, 2, 3).map { displayInfos[it] } for (displayInfo in displaysToRemove) { whenever(mockTopology.removeDisplay(displayInfo.displayId)).thenReturn(true) coordinator.onDisplayRemoved(displayInfo.displayId) } coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY) val captor = ArgumentCaptor.forClass(SparseIntArray::class.java) verify(mockTopologyCopy, times(displaysToRemove.size)).getGraph(captor.capture()) val densities = captor.value assertThat(densities.size()).isEqualTo(displayInfos.size - displaysToRemove.size) for (displayInfo in displaysToRemove) { assertThat(densities.get(displayInfo.displayId)).isEqualTo(0) } verify(mockTopologyChangedCallback).invoke(mockTopologyCopy) verify(mockTopologyStore).restoreTopology(mockTopology) verify(mockTopologyChangedCallback, times(displaysToRemove.size)).invoke( android.util.Pair( mockTopologyCopy, mockTopologyGraph ) ) verify(mockTopologyStore, times(displaysToRemove.size)).restoreTopology(mockTopology) } @Test fun removeDisplay_notChanged() { whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(false) coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY) for (displayInfo in displayInfos) { coordinator.onDisplayRemoved(displayInfo.displayId) } verify(mockTopologyChangedCallback, never()).invoke(any()) verify(mockTopologyStore, never()).restoreTopology(any()) Loading @@ -149,12 +250,20 @@ class DisplayTopologyCoordinatorTest { fun setTopology_normalize() { val topology = mock<DisplayTopology>() val topologyCopy = mock<DisplayTopology>() val topologyGraph = mock<DisplayTopologyGraph>() whenever(topology.copy()).thenReturn(topologyCopy) whenever(topologyCopy.getGraph(any())).thenReturn(topologyGraph) whenever(mockTopologyStore.saveTopology(topology)).thenReturn(true) coordinator.topology = topology verify(topology).normalize() verify(mockTopologyChangedCallback).invoke(topologyCopy) verify(mockTopologyChangedCallback).invoke( android.util.Pair( topologyCopy, topologyGraph ) ) verify(mockTopologyStore).saveTopology(topology) verify(mockTopologySavedCallback).invoke() } Loading Loading
core/java/android/hardware/display/DisplayTopology.java +1 −1 Original line number Diff line number Diff line Loading @@ -750,7 +750,7 @@ public final class DisplayTopology implements Parcelable { new SparseArray<>(); for (int id : displayIds) { if (densityPerDisplay.get(id) == 0) { Slog.w(TAG, "Cannot construct graph, no density for display " + id); Slog.e(TAG, "Cannot construct graph, no density for display " + id); return null; } adjacentDisplaysPerId.append(id, new ArrayList<>(Math.min(10, displayIds.size()))); Loading
services/core/java/com/android/server/display/DisplayManagerService.java +10 −1 Original line number Diff line number Diff line Loading @@ -107,6 +107,7 @@ import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayTopologyGraph; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; Loading Loading @@ -292,6 +293,7 @@ public final class DisplayManagerService extends SystemService { private final DisplayModeDirector mDisplayModeDirector; private final ExternalDisplayPolicy mExternalDisplayPolicy; private WindowManagerInternal mWindowManagerInternal; @Nullable private InputManagerInternal mInputManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private final UidImportanceListener mUidImportanceListener = new UidImportanceListener(); Loading Loading @@ -680,8 +682,15 @@ public final class DisplayManagerService extends SystemService { mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector()); if (mFlags.isDisplayTopologyEnabled()) { final var backupManager = new BackupManager(mContext); Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback = update -> { if (mInputManagerInternal != null) { mInputManagerInternal.setDisplayTopology(update.second); } deliverTopologyUpdate(update.first); }; mDisplayTopologyCoordinator = new DisplayTopologyCoordinator( this::isExtendedDisplayEnabled, this::deliverTopologyUpdate, this::isExtendedDisplayEnabled, topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged); } else { mDisplayTopologyCoordinator = null; Loading
services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +23 −5 Original line number Diff line number Diff line Loading @@ -19,8 +19,11 @@ package com.android.server.display; import static android.hardware.display.DisplayTopology.pxToDp; import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayTopologyGraph; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.Display; import android.view.DisplayInfo; Loading Loading @@ -53,6 +56,12 @@ class DisplayTopologyCoordinator { @GuardedBy("mSyncRoot") private DisplayTopology mTopology; // Map from logical display ID to logical display density. Should always be consistent with // mTopology. @GuardedBy("mSyncRoot") private final SparseIntArray mDensities = new SparseIntArray(); @GuardedBy("mSyncRoot") private final Map<String, Integer> mUniqueIdToDisplayIdMapping = new HashMap<>(); Loading @@ -69,13 +78,13 @@ class DisplayTopologyCoordinator { * Should be invoked from the corresponding executor. * A copy of the topology should be sent that will not be modified by the system. */ private final Consumer<DisplayTopology> mOnTopologyChangedCallback; private final Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> mOnTopologyChangedCallback; private final Executor mTopologyChangeExecutor; private final DisplayManagerService.SyncRoot mSyncRoot; private final Runnable mTopologySavedCallback; DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayEnabled, Consumer<DisplayTopology> onTopologyChangedCallback, Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback, Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot, Runnable topologySavedCallback) { this(new Injector(), isExtendedDisplayEnabled, onTopologyChangedCallback, Loading @@ -84,7 +93,7 @@ class DisplayTopologyCoordinator { @VisibleForTesting DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayEnabled, Consumer<DisplayTopology> onTopologyChangedCallback, Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback, Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot, Runnable topologySavedCallback) { mTopology = injector.getTopology(); Loading @@ -107,7 +116,7 @@ class DisplayTopologyCoordinator { } synchronized (mSyncRoot) { addDisplayIdMappingLocked(info); mDensities.put(info.displayId, info.logicalDensityDpi); mTopology.addDisplay(info.displayId, getWidth(info), getHeight(info)); restoreTopologyLocked(); sendTopologyUpdateLocked(); Loading @@ -119,7 +128,13 @@ class DisplayTopologyCoordinator { * @param info The new display info */ void onDisplayChanged(DisplayInfo info) { if (!isDisplayAllowedInTopology(info)) { return; } synchronized (mSyncRoot) { if (mDensities.indexOfKey(info.displayId) >= 0) { mDensities.put(info.displayId, info.logicalDensityDpi); } if (mTopology.updateDisplay(info.displayId, getWidth(info), getHeight(info))) { sendTopologyUpdateLocked(); } Loading @@ -132,6 +147,7 @@ class DisplayTopologyCoordinator { */ void onDisplayRemoved(int displayId) { synchronized (mSyncRoot) { mDensities.delete(displayId); if (mTopology.removeDisplay(displayId)) { removeDisplayIdMappingLocked(displayId); restoreTopologyLocked(); Loading Loading @@ -256,7 +272,9 @@ class DisplayTopologyCoordinator { @GuardedBy("mSyncRoot") private void sendTopologyUpdateLocked() { DisplayTopology copy = mTopology.copy(); mTopologyChangeExecutor.execute(() -> mOnTopologyChangedCallback.accept(copy)); SparseIntArray densities = mDensities.clone(); mTopologyChangeExecutor.execute(() -> mOnTopologyChangedCallback.accept( new Pair<>(copy, copy.getGraph(densities)))); } @VisibleForTesting Loading
services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +11 −1 Original line number Diff line number Diff line Loading @@ -107,6 +107,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayOffloader; import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayTopologyGraph; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; Loading Loading @@ -136,6 +137,7 @@ import android.provider.Settings.SettingNotFoundException; import android.test.mock.MockContentResolver; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.Spline; import android.view.ContentRecordingSession; import android.view.Display; Loading Loading @@ -3875,8 +3877,16 @@ public class DisplayManagerServiceTest { displayManager.new BinderService(); registerDefaultDisplays(displayManager); initDisplayPowerController(localService); displayManager.windowManagerAndInputReady(); DisplayTopology topology = mock(DisplayTopology.class); when(topology.copy()).thenReturn(topology); DisplayTopologyGraph graph = mock(DisplayTopologyGraph.class); when(topology.getGraph(any(SparseIntArray.class))).thenReturn(graph); displayManagerBinderService.setDisplayTopology(topology); waitForIdleHandler(displayManager.getDisplayHandler()); displayManagerBinderService.setDisplayTopology(new DisplayTopology()); verify(mMockInputManagerInternal).setDisplayTopology(graph); } @Test Loading
services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt +138 −29 Original line number Diff line number Diff line Loading @@ -18,39 +18,51 @@ package com.android.server.display import android.hardware.display.DisplayTopology import android.hardware.display.DisplayTopology.pxToDp import android.hardware.display.DisplayTopologyGraph import android.util.SparseArray import android.util.SparseIntArray import android.view.Display import android.view.DisplayInfo import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.any import org.mockito.kotlin.clearInvocations import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever class DisplayTopologyCoordinatorTest { private lateinit var coordinator: DisplayTopologyCoordinator private val displayInfo = DisplayInfo() private lateinit var displayInfos: List<DisplayInfo> private val topologyChangeExecutor = Runnable::run private val mockTopologyStore = mock<DisplayTopologyStore>() private val mockTopology = mock<DisplayTopology>() private val mockTopologyCopy = mock<DisplayTopology>() private val mockTopologyGraph = mock<DisplayTopologyGraph>() private val mockIsExtendedDisplayEnabled = mock<() -> Boolean>() private val mockTopologySavedCallback = mock<() -> Unit>() private val mockTopologyChangedCallback = mock<(DisplayTopology) -> Unit>() private val mockTopologyChangedCallback = mock<(android.util.Pair<DisplayTopology, DisplayTopologyGraph>) -> Unit>() @Before fun setUp() { displayInfo.displayId = Display.DEFAULT_DISPLAY displayInfo.logicalWidth = 300 displayInfo.logicalHeight = 200 displayInfo.logicalDensityDpi = 100 displayInfos = (1..10).map { i -> val info = DisplayInfo() info.displayId = i info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP info.logicalWidth = i * 300 info.logicalHeight = i * 200 info.logicalDensityDpi = i * 100 return@map info } val injector = object : DisplayTopologyCoordinator.Injector() { override fun getTopology() = mockTopology Loading @@ -62,6 +74,7 @@ class DisplayTopologyCoordinatorTest { } whenever(mockIsExtendedDisplayEnabled()).thenReturn(true) whenever(mockTopology.copy()).thenReturn(mockTopologyCopy) whenever(mockTopologyCopy.getGraph(any())).thenReturn(mockTopologyGraph) coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayEnabled, mockTopologyChangedCallback, topologyChangeExecutor, DisplayManagerService.SyncRoot(), mockTopologySavedCallback) Loading @@ -69,20 +82,45 @@ class DisplayTopologyCoordinatorTest { @Test fun addDisplay() { displayInfos.forEachIndexed { i, displayInfo -> coordinator.onDisplayAdded(displayInfo) val widthDp = pxToDp(displayInfo.logicalWidth.toFloat(), displayInfo.logicalDensityDpi) val heightDp = pxToDp(displayInfo.logicalHeight.toFloat(), displayInfo.logicalDensityDpi) val heightDp = pxToDp(displayInfo.logicalHeight.toFloat(), displayInfo.logicalDensityDpi) verify(mockTopology).addDisplay(displayInfo.displayId, widthDp, heightDp) verify(mockTopologyChangedCallback).invoke(mockTopologyCopy) verify(mockTopologyStore).restoreTopology(mockTopology) } val captor = ArgumentCaptor.forClass(SparseIntArray::class.java) verify(mockTopologyCopy, times(displayInfos.size)).getGraph(captor.capture()) val densities = captor.value assertThat(densities.size()).isEqualTo(displayInfos.size) for (displayInfo in displayInfos) { assertThat(densities.get(displayInfo.displayId)) .isEqualTo(displayInfo.logicalDensityDpi) } verify(mockTopologyChangedCallback, times(displayInfos.size)).invoke( android.util.Pair( mockTopologyCopy, mockTopologyGraph ) ) verify(mockTopologyStore, times(displayInfos.size)).restoreTopology(mockTopology) // Clear invocations for other tests that call this method clearInvocations(mockTopologyCopy) clearInvocations(mockTopologyChangedCallback) clearInvocations(mockTopologyStore) } @Test fun addDisplay_extendedDisplaysDisabled() { whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) for (displayInfo in displayInfos) { coordinator.onDisplayAdded(displayInfo) } verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyChangedCallback, never()).invoke(any()) Loading @@ -91,9 +129,9 @@ class DisplayTopologyCoordinatorTest { @Test fun addDisplay_notInDefaultDisplayGroup() { displayInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 displayInfos[0].displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 coordinator.onDisplayAdded(displayInfo) coordinator.onDisplayAdded(displayInfos[0]) verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyChangedCallback, never()).invoke(any()) Loading @@ -102,39 +140,102 @@ class DisplayTopologyCoordinatorTest { @Test fun updateDisplay() { whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat())) whenever(mockTopology.updateDisplay(eq(displayInfos[0].displayId), anyFloat(), anyFloat())) .thenReturn(true) addDisplay() coordinator.onDisplayChanged(displayInfo) displayInfos[0].logicalDensityDpi += 10 coordinator.onDisplayChanged(displayInfos[0]) verify(mockTopologyChangedCallback).invoke(mockTopologyCopy) val captor = ArgumentCaptor.forClass(SparseIntArray::class.java) verify(mockTopologyCopy).getGraph(captor.capture()) val densities = captor.value assertThat(densities.size()).isEqualTo(displayInfos.size) assertThat(densities.get(displayInfos[0].displayId)) .isEqualTo(displayInfos[0].logicalDensityDpi) verify(mockTopologyChangedCallback).invoke( android.util.Pair( mockTopologyCopy, mockTopologyGraph ) ) } @Test fun updateDisplay_notChanged() { whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat())) .thenReturn(false) addDisplay() for (displayInfo in displayInfos) { coordinator.onDisplayChanged(displayInfo) } // Try to update a display that does not exist val info = DisplayInfo() info.displayId = 100 coordinator.onDisplayChanged(info) verify(mockTopologyCopy, never()).getGraph(any()) verify(mockTopologyChangedCallback, never()).invoke(any()) } @Test fun updateDisplay_extendedDisplaysDisabled() { whenever(mockIsExtendedDisplayEnabled()).thenReturn(false) for (displayInfo in displayInfos) { coordinator.onDisplayAdded(displayInfo) } verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyCopy, never()).getGraph(any()) verify(mockTopologyChangedCallback, never()).invoke(any()) } @Test fun updateDisplay_notInDefaultDisplayGroup() { displayInfos[0].displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1 coordinator.onDisplayAdded(displayInfos[0]) verify(mockTopology, never()).updateDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyCopy, never()).getGraph(any()) verify(mockTopologyChangedCallback, never()).invoke(any()) } @Test fun removeDisplay() { whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(true) addDisplay() val displaysToRemove = listOf(0, 2, 3).map { displayInfos[it] } for (displayInfo in displaysToRemove) { whenever(mockTopology.removeDisplay(displayInfo.displayId)).thenReturn(true) coordinator.onDisplayRemoved(displayInfo.displayId) } coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY) val captor = ArgumentCaptor.forClass(SparseIntArray::class.java) verify(mockTopologyCopy, times(displaysToRemove.size)).getGraph(captor.capture()) val densities = captor.value assertThat(densities.size()).isEqualTo(displayInfos.size - displaysToRemove.size) for (displayInfo in displaysToRemove) { assertThat(densities.get(displayInfo.displayId)).isEqualTo(0) } verify(mockTopologyChangedCallback).invoke(mockTopologyCopy) verify(mockTopologyStore).restoreTopology(mockTopology) verify(mockTopologyChangedCallback, times(displaysToRemove.size)).invoke( android.util.Pair( mockTopologyCopy, mockTopologyGraph ) ) verify(mockTopologyStore, times(displaysToRemove.size)).restoreTopology(mockTopology) } @Test fun removeDisplay_notChanged() { whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(false) coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY) for (displayInfo in displayInfos) { coordinator.onDisplayRemoved(displayInfo.displayId) } verify(mockTopologyChangedCallback, never()).invoke(any()) verify(mockTopologyStore, never()).restoreTopology(any()) Loading @@ -149,12 +250,20 @@ class DisplayTopologyCoordinatorTest { fun setTopology_normalize() { val topology = mock<DisplayTopology>() val topologyCopy = mock<DisplayTopology>() val topologyGraph = mock<DisplayTopologyGraph>() whenever(topology.copy()).thenReturn(topologyCopy) whenever(topologyCopy.getGraph(any())).thenReturn(topologyGraph) whenever(mockTopologyStore.saveTopology(topology)).thenReturn(true) coordinator.topology = topology verify(topology).normalize() verify(mockTopologyChangedCallback).invoke(topologyCopy) verify(mockTopologyChangedCallback).invoke( android.util.Pair( topologyCopy, topologyGraph ) ) verify(mockTopologyStore).saveTopology(topology) verify(mockTopologySavedCallback).invoke() } Loading