Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88bd95e4 authored by Piotr Wilczyński's avatar Piotr Wilczyński Committed by Android (Google) Code Review
Browse files

Merge "Update topology when display changes" into main

parents 7c497863 d6b9ae17
Loading
Loading
Loading
Loading
+29 −4
Original line number Diff line number Diff line
@@ -128,15 +128,39 @@ public final class DisplayTopology implements Parcelable {
        addDisplay(displayId, width, height, /* shouldLog= */ true);
    }

    /**
     * Update the size of a display and normalize the topology.
     * @param displayId The logical display ID
     * @param width The new width
     * @param height The new height
     * @return True if the topology has changed.
     */
    public boolean updateDisplay(int displayId, float width, float height) {
        TreeNode display = findDisplay(displayId, mRoot);
        if (display == null) {
            return false;
        }
        if (floatEquals(display.mWidth, width) && floatEquals(display.mHeight, height)) {
            return false;
        }
        display.mWidth = width;
        display.mHeight = height;
        normalize();
        Slog.i(TAG, "Display with ID " + displayId + " updated, new width: " + width
                + ", new height: " + height);
        return true;
    }

    /**
     * Remove a display from the topology.
     * The default topology is created from the remaining displays, as if they were reconnected
     * one by one.
     * @param displayId The logical display ID
     * @return True if the display was present in the topology and removed.
     */
    public void removeDisplay(int displayId) {
    public boolean removeDisplay(int displayId) {
        if (findDisplay(displayId, mRoot) == null) {
            return;
            return false;
        }
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.add(mRoot);
@@ -159,6 +183,7 @@ public final class DisplayTopology implements Parcelable {
        } else {
            Slog.i(TAG, "Display with ID " + displayId + " removed");
        }
        return true;
    }

    /**
@@ -685,12 +710,12 @@ public final class DisplayTopology implements Parcelable {
        /**
         * The width of the display in density-independent pixels (dp).
         */
        private final float mWidth;
        private float mWidth;

        /**
         * The height of the display in density-independent pixels (dp).
         */
        private final float mHeight;
        private float mHeight;

        /**
         * The position of this display relative to its parent.
+78 −8
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ class DisplayTopologyTest {
        verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1)

        val display2 = display1.children[0]
        verifyDisplay(display1.children[0], displayId2, width2, height2, POSITION_TOP,
        verifyDisplay(display2, displayId2, width2, height2, POSITION_TOP,
            offset = width1 / 2 - width2 / 2, noOfChildren = 1)

        var display = display2
@@ -98,6 +98,76 @@ class DisplayTopologyTest {
        }
    }

    @Test
    fun updateDisplay() {
        val displayId = 1
        val width = 800f
        val height = 600f

        val newWidth = 1000f
        val newHeight = 500f
        topology.addDisplay(displayId, width, height)
        assertThat(topology.updateDisplay(displayId, newWidth, newHeight)).isTrue()

        assertThat(topology.primaryDisplayId).isEqualTo(displayId)
        verifyDisplay(topology.root!!, displayId, newWidth, newHeight, noOfChildren = 0)
    }

    @Test
    fun updateDisplay_notUpdated() {
        val displayId = 1
        val width = 800f
        val height = 600f
        topology.addDisplay(displayId, width, height)

        // Same size
        assertThat(topology.updateDisplay(displayId, width, height)).isFalse()

        // Display doesn't exist
        assertThat(topology.updateDisplay(/* displayId= */ 100, width, height)).isFalse()

        assertThat(topology.primaryDisplayId).isEqualTo(displayId)
        verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
    }

    @Test
    fun updateDisplayDoesNotAffectDefaultTopology() {
        val width1 = 700f
        val height = 600f
        topology.addDisplay(/* displayId= */ 1, width1, height)

        val width2 = 800f
        val noOfDisplays = 30
        for (i in 2..noOfDisplays) {
            topology.addDisplay(/* displayId= */ i, width2, height)
        }

        val displaysToUpdate = arrayOf(3, 7, 18)
        val newWidth = 1000f
        val newHeight = 1500f
        for (i in displaysToUpdate) {
            assertThat(topology.updateDisplay(/* displayId= */ i, newWidth, newHeight)).isTrue()
        }

        assertThat(topology.primaryDisplayId).isEqualTo(1)

        val display1 = topology.root!!
        verifyDisplay(display1, id = 1, width1, height, noOfChildren = 1)

        val display2 = display1.children[0]
        verifyDisplay(display2, id = 2, width2, height, POSITION_TOP,
            offset = width1 / 2 - width2 / 2, noOfChildren = 1)

        var display = display2
        for (i in 3..noOfDisplays) {
            display = display.children[0]
            // The last display should have no children
            verifyDisplay(display, id = i, if (i in displaysToUpdate) newWidth else width2,
                if (i in displaysToUpdate) newHeight else height, POSITION_RIGHT, offset = 0f,
                noOfChildren = if (i < noOfDisplays) 1 else 0)
        }
    }

    @Test
    fun removeDisplays() {
        val displayId1 = 1
@@ -117,7 +187,7 @@ class DisplayTopologyTest {
        }

        var removedDisplays = arrayOf(20)
        topology.removeDisplay(20)
        assertThat(topology.removeDisplay(20)).isTrue()

        assertThat(topology.primaryDisplayId).isEqualTo(displayId1)

@@ -139,11 +209,11 @@ class DisplayTopologyTest {
                noOfChildren = if (i < noOfDisplays) 1 else 0)
        }

        topology.removeDisplay(22)
        assertThat(topology.removeDisplay(22)).isTrue()
        removedDisplays += 22
        topology.removeDisplay(23)
        assertThat(topology.removeDisplay(23)).isTrue()
        removedDisplays += 23
        topology.removeDisplay(25)
        assertThat(topology.removeDisplay(25)).isTrue()
        removedDisplays += 25

        assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -174,7 +244,7 @@ class DisplayTopologyTest {
        val height = 600f

        topology.addDisplay(displayId, width, height)
        topology.removeDisplay(displayId)
        assertThat(topology.removeDisplay(displayId)).isTrue()

        assertThat(topology.primaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
        assertThat(topology.root).isNull()
@@ -187,7 +257,7 @@ class DisplayTopologyTest {
        val height = 600f

        topology.addDisplay(displayId, width, height)
        topology.removeDisplay(3)
        assertThat(topology.removeDisplay(3)).isFalse()

        assertThat(topology.primaryDisplayId).isEqualTo(displayId)
        verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
@@ -203,7 +273,7 @@ class DisplayTopologyTest {
        topology = DisplayTopology(/* root= */ null, displayId2)
        topology.addDisplay(displayId1, width, height)
        topology.addDisplay(displayId2, width, height)
        topology.removeDisplay(displayId2)
        assertThat(topology.removeDisplay(displayId2)).isTrue()

        assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
        verifyDisplay(topology.root!!, displayId1, width, height, noOfChildren = 0)
+4 −0
Original line number Diff line number Diff line
@@ -2400,6 +2400,10 @@ public final class DisplayManagerService extends SystemService {
        sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);

        applyDisplayChangedLocked(display);

        if (mDisplayTopologyCoordinator != null) {
            mDisplayTopologyCoordinator.onDisplayChanged(display.getDisplayInfoLocked());
        }
    }

    private void applyDisplayChangedLocked(@NonNull LogicalDisplay display) {
+15 −2
Original line number Diff line number Diff line
@@ -84,16 +84,29 @@ class DisplayTopologyCoordinator {
        }
    }

    /**
     * Update the topology with display changes.
     * @param info The new display info
     */
    void onDisplayChanged(DisplayInfo info) {
        synchronized (mSyncRoot) {
            if (mTopology.updateDisplay(info.displayId, getWidth(info), getHeight(info))) {
                sendTopologyUpdateLocked();
            }
        }
    }

    /**
     * Remove a display from the topology.
     * @param displayId The logical display ID
     */
    void onDisplayRemoved(int displayId) {
        synchronized (mSyncRoot) {
            mTopology.removeDisplay(displayId);
            if (mTopology.removeDisplay(displayId)) {
                sendTopologyUpdateLocked();
            }
        }
    }

    /**
     * @return A deep copy of the topology.
+40 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import org.junit.Test
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -43,7 +44,7 @@ class DisplayTopologyCoordinatorTest {

    @Before
    fun setUp() {
        displayInfo.displayId = 2
        displayInfo.displayId = Display.DEFAULT_DISPLAY
        displayInfo.logicalWidth = 300
        displayInfo.logicalHeight = 200
        displayInfo.logicalDensityDpi = 100
@@ -89,6 +90,44 @@ class DisplayTopologyCoordinatorTest {
        verify(mockTopologyChangedCallback, never()).invoke(any())
    }

    @Test
    fun updateDisplay() {
        whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
            .thenReturn(true)

        coordinator.onDisplayChanged(displayInfo)

        verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
    }

    @Test
    fun updateDisplay_notChanged() {
        whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
            .thenReturn(false)

        coordinator.onDisplayChanged(displayInfo)

        verify(mockTopologyChangedCallback, never()).invoke(any())
    }

    @Test
    fun removeDisplay() {
        whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(true)

        coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)

        verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
    }

    @Test
    fun removeDisplay_notChanged() {
        whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(false)

        coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)

        verify(mockTopologyChangedCallback, never()).invoke(any())
    }

    @Test
    fun getTopology_copy() {
        assertThat(coordinator.topology).isEqualTo(mockTopologyCopy)