diff options
author | 2024-11-29 14:00:34 +0000 | |
---|---|---|
committer | 2024-11-29 14:00:34 +0000 | |
commit | f2290c21dbd263254e85e46a1467d73cc69b5aa5 (patch) | |
tree | 47d0f97f6b2cc370efc644a5a15df879a60172e5 | |
parent | ed7d191a5974225d59b62e4c0efd92ca6a33d3a8 (diff) | |
parent | 060d32262d56358d7bf0c5c98c423a4c599d5cf4 (diff) |
Merge "Get topology copy, set topology normalize" into main
4 files changed, 285 insertions, 323 deletions
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java index 0e53d873e43c..54d0dd0eb8f8 100644 --- a/core/java/android/hardware/display/DisplayTopology.java +++ b/core/java/android/hardware/display/DisplayTopology.java @@ -28,6 +28,7 @@ import android.graphics.RectF; import android.os.Parcel; import android.os.Parcelable; import android.util.IndentingPrintWriter; +import android.util.MathUtils; import android.util.Pair; import android.util.Slog; import android.view.Display; @@ -284,6 +285,146 @@ public final class DisplayTopology implements Parcelable { } /** + * Clamp offsets and remove any overlaps between displays. + */ + public void normalize() { + if (mRoot == null) { + return; + } + clampOffsets(mRoot); + + Map<TreeNode, RectF> bounds = new HashMap<>(); + Map<TreeNode, Integer> depths = new HashMap<>(); + Map<TreeNode, TreeNode> parents = new HashMap<>(); + getInfo(bounds, depths, parents, mRoot, /* x= */ 0, /* y= */ 0, /* depth= */ 0); + + // Sort the displays first by their depth in the tree, then by the distance of their top + // left point from the root display's origin (0, 0). This way we process the displays + // starting at the root and we push out a display if necessary. + Comparator<TreeNode> comparator = (d1, d2) -> { + if (d1 == d2) { + return 0; + } + + int compareDepths = Integer.compare(depths.get(d1), depths.get(d2)); + if (compareDepths != 0) { + return compareDepths; + } + + RectF bounds1 = bounds.get(d1); + RectF bounds2 = bounds.get(d2); + return Double.compare(Math.hypot(bounds1.left, bounds1.top), + Math.hypot(bounds2.left, bounds2.top)); + }; + List<TreeNode> displays = new ArrayList<>(bounds.keySet()); + displays.sort(comparator); + + for (int i = 1; i < displays.size(); i++) { + TreeNode targetDisplay = displays.get(i); + TreeNode lastIntersectingSourceDisplay = null; + float lastOffsetX = 0; + float lastOffsetY = 0; + + for (int j = 0; j < i; j++) { + TreeNode sourceDisplay = displays.get(j); + RectF sourceBounds = bounds.get(sourceDisplay); + RectF targetBounds = bounds.get(targetDisplay); + + if (!RectF.intersects(sourceBounds, targetBounds)) { + continue; + } + + // Find the offset by which to move the display. Pick the smaller one among the x + // and y axes. + float offsetX = targetBounds.left >= 0 + ? sourceBounds.right - targetBounds.left + : sourceBounds.left - targetBounds.right; + float offsetY = targetBounds.top >= 0 + ? sourceBounds.bottom - targetBounds.top + : sourceBounds.top - targetBounds.bottom; + if (Math.abs(offsetX) <= Math.abs(offsetY)) { + targetBounds.left += offsetX; + targetBounds.right += offsetX; + // We need to also update the offset in the tree + if (targetDisplay.mPosition == POSITION_TOP + || targetDisplay.mPosition == POSITION_BOTTOM) { + targetDisplay.mOffset += offsetX; + } + offsetY = 0; + } else { + targetBounds.top += offsetY; + targetBounds.bottom += offsetY; + // We need to also update the offset in the tree + if (targetDisplay.mPosition == POSITION_LEFT + || targetDisplay.mPosition == POSITION_RIGHT) { + targetDisplay.mOffset += offsetY; + } + offsetX = 0; + } + + lastIntersectingSourceDisplay = sourceDisplay; + lastOffsetX = offsetX; + lastOffsetY = offsetY; + } + + // Now re-parent the target display to the last intersecting source display if it no + // longer touches its parent. + if (lastIntersectingSourceDisplay == null) { + // There was no overlap. + continue; + } + TreeNode parent = parents.get(targetDisplay); + if (parent == lastIntersectingSourceDisplay) { + // The displays are moved in such a way that they're adjacent to the intersecting + // display. If the last intersecting display happens to be the parent then we + // already know that the display is adjacent to its parent. + continue; + } + + RectF childBounds = bounds.get(targetDisplay); + RectF parentBounds = bounds.get(parent); + // Check that the edges are on the same line + boolean areTouching = switch (targetDisplay.mPosition) { + case POSITION_LEFT -> floatEquals(parentBounds.left, childBounds.right); + case POSITION_RIGHT -> floatEquals(parentBounds.right, childBounds.left); + case POSITION_TOP -> floatEquals(parentBounds.top, childBounds.bottom); + case POSITION_BOTTOM -> floatEquals(parentBounds.bottom, childBounds.top); + default -> throw new IllegalStateException( + "Unexpected value: " + targetDisplay.mPosition); + }; + // Check that the offset is within bounds + areTouching &= switch (targetDisplay.mPosition) { + case POSITION_LEFT, POSITION_RIGHT -> + childBounds.bottom + EPSILON >= parentBounds.top + && childBounds.top <= parentBounds.bottom + EPSILON; + case POSITION_TOP, POSITION_BOTTOM -> + childBounds.right + EPSILON >= parentBounds.left + && childBounds.left <= parentBounds.right + EPSILON; + default -> throw new IllegalStateException( + "Unexpected value: " + targetDisplay.mPosition); + }; + + if (!areTouching) { + // Re-parent the display. + parent.mChildren.remove(targetDisplay); + RectF lastIntersectingSourceDisplayBounds = + bounds.get(lastIntersectingSourceDisplay); + lastIntersectingSourceDisplay.mChildren.add(targetDisplay); + + if (lastOffsetX != 0) { + targetDisplay.mPosition = lastOffsetX > 0 ? POSITION_RIGHT : POSITION_LEFT; + targetDisplay.mOffset = + childBounds.top - lastIntersectingSourceDisplayBounds.top; + } else if (lastOffsetY != 0) { + targetDisplay.mPosition = lastOffsetY > 0 ? POSITION_BOTTOM : POSITION_TOP; + targetDisplay.mOffset = + childBounds.left - lastIntersectingSourceDisplayBounds.left; + } + } + } + } + + /** * @return A deep copy of the topology that will not be modified by the system. */ public DisplayTopology copy() { @@ -442,145 +583,6 @@ public final class DisplayTopology implements Parcelable { } /** - * Update the topology to remove any overlaps between displays. - */ - @VisibleForTesting - public void normalize() { - if (mRoot == null) { - return; - } - Map<TreeNode, RectF> bounds = new HashMap<>(); - Map<TreeNode, Integer> depths = new HashMap<>(); - Map<TreeNode, TreeNode> parents = new HashMap<>(); - getInfo(bounds, depths, parents, mRoot, /* x= */ 0, /* y= */ 0, /* depth= */ 0); - - // Sort the displays first by their depth in the tree, then by the distance of their top - // left point from the root display's origin (0, 0). This way we process the displays - // starting at the root and we push out a display if necessary. - Comparator<TreeNode> comparator = (d1, d2) -> { - if (d1 == d2) { - return 0; - } - - int compareDepths = Integer.compare(depths.get(d1), depths.get(d2)); - if (compareDepths != 0) { - return compareDepths; - } - - RectF bounds1 = bounds.get(d1); - RectF bounds2 = bounds.get(d2); - return Double.compare(Math.hypot(bounds1.left, bounds1.top), - Math.hypot(bounds2.left, bounds2.top)); - }; - List<TreeNode> displays = new ArrayList<>(bounds.keySet()); - displays.sort(comparator); - - for (int i = 1; i < displays.size(); i++) { - TreeNode targetDisplay = displays.get(i); - TreeNode lastIntersectingSourceDisplay = null; - float lastOffsetX = 0; - float lastOffsetY = 0; - - for (int j = 0; j < i; j++) { - TreeNode sourceDisplay = displays.get(j); - RectF sourceBounds = bounds.get(sourceDisplay); - RectF targetBounds = bounds.get(targetDisplay); - - if (!RectF.intersects(sourceBounds, targetBounds)) { - continue; - } - - // Find the offset by which to move the display. Pick the smaller one among the x - // and y axes. - float offsetX = targetBounds.left >= 0 - ? sourceBounds.right - targetBounds.left - : sourceBounds.left - targetBounds.right; - float offsetY = targetBounds.top >= 0 - ? sourceBounds.bottom - targetBounds.top - : sourceBounds.top - targetBounds.bottom; - if (Math.abs(offsetX) <= Math.abs(offsetY)) { - targetBounds.left += offsetX; - targetBounds.right += offsetX; - // We need to also update the offset in the tree - if (targetDisplay.mPosition == POSITION_TOP - || targetDisplay.mPosition == POSITION_BOTTOM) { - targetDisplay.mOffset += offsetX; - } - offsetY = 0; - } else { - targetBounds.top += offsetY; - targetBounds.bottom += offsetY; - // We need to also update the offset in the tree - if (targetDisplay.mPosition == POSITION_LEFT - || targetDisplay.mPosition == POSITION_RIGHT) { - targetDisplay.mOffset += offsetY; - } - offsetX = 0; - } - - lastIntersectingSourceDisplay = sourceDisplay; - lastOffsetX = offsetX; - lastOffsetY = offsetY; - } - - // Now re-parent the target display to the last intersecting source display if it no - // longer touches its parent. - if (lastIntersectingSourceDisplay == null) { - // There was no overlap. - continue; - } - TreeNode parent = parents.get(targetDisplay); - if (parent == lastIntersectingSourceDisplay) { - // The displays are moved in such a way that they're adjacent to the intersecting - // display. If the last intersecting display happens to be the parent then we - // already know that the display is adjacent to its parent. - continue; - } - - RectF childBounds = bounds.get(targetDisplay); - RectF parentBounds = bounds.get(parent); - // Check that the edges are on the same line - boolean areTouching = switch (targetDisplay.mPosition) { - case POSITION_LEFT -> floatEquals(parentBounds.left, childBounds.right); - case POSITION_RIGHT -> floatEquals(parentBounds.right, childBounds.left); - case POSITION_TOP -> floatEquals(parentBounds.top, childBounds.bottom); - case POSITION_BOTTOM -> floatEquals(parentBounds.bottom, childBounds.top); - default -> throw new IllegalStateException( - "Unexpected value: " + targetDisplay.mPosition); - }; - // Check that the offset is within bounds - areTouching &= switch (targetDisplay.mPosition) { - case POSITION_LEFT, POSITION_RIGHT -> - childBounds.bottom + EPSILON >= parentBounds.top - && childBounds.top <= parentBounds.bottom + EPSILON; - case POSITION_TOP, POSITION_BOTTOM -> - childBounds.right + EPSILON >= parentBounds.left - && childBounds.left <= parentBounds.right + EPSILON; - default -> throw new IllegalStateException( - "Unexpected value: " + targetDisplay.mPosition); - }; - - if (!areTouching) { - // Re-parent the display. - parent.mChildren.remove(targetDisplay); - RectF lastIntersectingSourceDisplayBounds = - bounds.get(lastIntersectingSourceDisplay); - lastIntersectingSourceDisplay.mChildren.add(targetDisplay); - - if (lastOffsetX != 0) { - targetDisplay.mPosition = lastOffsetX > 0 ? POSITION_RIGHT : POSITION_LEFT; - targetDisplay.mOffset = - childBounds.top - lastIntersectingSourceDisplayBounds.top; - } else if (lastOffsetY != 0) { - targetDisplay.mPosition = lastOffsetY > 0 ? POSITION_BOTTOM : POSITION_TOP; - targetDisplay.mOffset = - childBounds.left - lastIntersectingSourceDisplayBounds.left; - } - } - } - } - - /** * Tests whether two brightness float values are within a small enough tolerance * of each other. * @param a first float to compare @@ -605,6 +607,24 @@ public final class DisplayTopology implements Parcelable { return found; } + /** + * Ensure that the offsets of all displays within the given tree are within bounds. + * @param display The starting node + */ + private void clampOffsets(TreeNode display) { + if (display == null) { + return; + } + for (TreeNode child : display.mChildren) { + if (child.mPosition == POSITION_LEFT || child.mPosition == POSITION_RIGHT) { + child.mOffset = MathUtils.constrain(child.mOffset, -child.mHeight, display.mHeight); + } else if (child.mPosition == POSITION_TOP || child.mPosition == POSITION_BOTTOM) { + child.mOffset = MathUtils.constrain(child.mOffset, -child.mWidth, display.mWidth); + } + clampOffsets(child); + } + } + public static final class TreeNode implements Parcelable { public static final int POSITION_LEFT = 0; public static final int POSITION_TOP = 1; diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt index f584ab971d04..18e4fde280ec 100644 --- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt +++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt @@ -38,12 +38,7 @@ class DisplayTopologyTest { topology.addDisplay(displayId, width, height) assertThat(topology.primaryDisplayId).isEqualTo(displayId) - - val display = topology.root!! - assertThat(display.displayId).isEqualTo(displayId) - assertThat(display.width).isEqualTo(width) - assertThat(display.height).isEqualTo(height) - assertThat(display.children).isEmpty() + verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0) } @Test @@ -62,18 +57,9 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(displayId1) val display1 = topology.root!! - assertThat(display1.displayId).isEqualTo(displayId1) - assertThat(display1.width).isEqualTo(width1) - assertThat(display1.height).isEqualTo(height1) - assertThat(display1.children).hasSize(1) - - val display2 = display1.children[0] - assertThat(display2.displayId).isEqualTo(displayId2) - assertThat(display2.width).isEqualTo(width2) - assertThat(display2.height).isEqualTo(height2) - assertThat(display2.children).isEmpty() - assertThat(display2.position).isEqualTo(POSITION_TOP) - assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1) + verifyDisplay(display1.children[0], displayId2, width2, height2, POSITION_TOP, + offset = width1 / 2 - width2 / 2, noOfChildren = 0) } @Test @@ -97,29 +83,18 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(displayId1) val display1 = topology.root!! - assertThat(display1.displayId).isEqualTo(displayId1) - assertThat(display1.width).isEqualTo(width1) - assertThat(display1.height).isEqualTo(height1) - assertThat(display1.children).hasSize(1) + verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1) val display2 = display1.children[0] - assertThat(display2.displayId).isEqualTo(displayId2) - assertThat(display2.width).isEqualTo(width2) - assertThat(display2.height).isEqualTo(height2) - assertThat(display2.children).hasSize(1) - assertThat(display2.position).isEqualTo(POSITION_TOP) - assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + verifyDisplay(display1.children[0], displayId2, width2, height2, POSITION_TOP, + offset = width1 / 2 - width2 / 2, noOfChildren = 1) var display = display2 for (i in 3..noOfDisplays) { display = display.children[0] - assertThat(display.displayId).isEqualTo(i) - assertThat(display.width).isEqualTo(width1) - assertThat(display.height).isEqualTo(height1) // The last display should have no children - assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0) - assertThat(display.position).isEqualTo(POSITION_RIGHT) - assertThat(display.offset).isEqualTo(0) + verifyDisplay(display, id = i, width1, height1, POSITION_RIGHT, offset = 0f, + noOfChildren = if (i < noOfDisplays) 1 else 0) } } @@ -147,18 +122,11 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(displayId1) var display1 = topology.root!! - assertThat(display1.displayId).isEqualTo(displayId1) - assertThat(display1.width).isEqualTo(width1) - assertThat(display1.height).isEqualTo(height1) - assertThat(display1.children).hasSize(1) + verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1) var display2 = display1.children[0] - assertThat(display2.displayId).isEqualTo(displayId2) - assertThat(display2.width).isEqualTo(width2) - assertThat(display2.height).isEqualTo(height2) - assertThat(display2.children).hasSize(1) - assertThat(display2.position).isEqualTo(POSITION_TOP) - assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + verifyDisplay(display2, displayId2, width2, height2, POSITION_TOP, + offset = width1 / 2 - width2 / 2, noOfChildren = 1) var display = display2 for (i in 3..noOfDisplays) { @@ -166,13 +134,9 @@ class DisplayTopologyTest { continue } display = display.children[0] - assertThat(display.displayId).isEqualTo(i) - assertThat(display.width).isEqualTo(width1) - assertThat(display.height).isEqualTo(height1) // The last display should have no children - assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0) - assertThat(display.position).isEqualTo(POSITION_RIGHT) - assertThat(display.offset).isEqualTo(0) + verifyDisplay(display, id = i, width1, height1, POSITION_RIGHT, offset = 0f, + noOfChildren = if (i < noOfDisplays) 1 else 0) } topology.removeDisplay(22) @@ -185,18 +149,11 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(displayId1) display1 = topology.root!! - assertThat(display1.displayId).isEqualTo(displayId1) - assertThat(display1.width).isEqualTo(width1) - assertThat(display1.height).isEqualTo(height1) - assertThat(display1.children).hasSize(1) + verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1) display2 = display1.children[0] - assertThat(display2.displayId).isEqualTo(displayId2) - assertThat(display2.width).isEqualTo(width2) - assertThat(display2.height).isEqualTo(height2) - assertThat(display2.children).hasSize(1) - assertThat(display2.position).isEqualTo(POSITION_TOP) - assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + verifyDisplay(display2, displayId2, width2, height2, POSITION_TOP, + offset = width1 / 2 - width2 / 2, noOfChildren = 1) display = display2 for (i in 3..noOfDisplays) { @@ -204,13 +161,9 @@ class DisplayTopologyTest { continue } display = display.children[0] - assertThat(display.displayId).isEqualTo(i) - assertThat(display.width).isEqualTo(width1) - assertThat(display.height).isEqualTo(height1) // The last display should have no children - assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0) - assertThat(display.position).isEqualTo(POSITION_RIGHT) - assertThat(display.offset).isEqualTo(0) + verifyDisplay(display, id = i, width1, height1, POSITION_RIGHT, offset = 0f, + noOfChildren = if (i < noOfDisplays) 1 else 0) } } @@ -237,12 +190,7 @@ class DisplayTopologyTest { topology.removeDisplay(3) assertThat(topology.primaryDisplayId).isEqualTo(displayId) - - val display = topology.root!! - assertThat(display.displayId).isEqualTo(displayId) - assertThat(display.width).isEqualTo(width) - assertThat(display.height).isEqualTo(height) - assertThat(display.children).isEmpty() + verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0) } @Test @@ -258,11 +206,46 @@ class DisplayTopologyTest { topology.removeDisplay(displayId2) assertThat(topology.primaryDisplayId).isEqualTo(displayId1) - val display = topology.root!! - assertThat(display.displayId).isEqualTo(displayId1) - assertThat(display.width).isEqualTo(width) - assertThat(display.height).isEqualTo(height) - assertThat(display.children).isEmpty() + verifyDisplay(topology.root!!, displayId1, width, height, noOfChildren = 0) + } + + @Test + fun normalization_clampsOffsets() { + val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, + /* height= */ 600f, /* position= */ 0, /* offset= */ 0f) + + val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f, + /* height= */ 200f, POSITION_RIGHT, /* offset= */ 800f) + display1.addChild(display2) + + val primaryDisplayId = 3 + val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, + /* height= */ 200f, POSITION_LEFT, /* offset= */ -300f) + display1.addChild(display3) + + val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f, + /* height= */ 600f, POSITION_TOP, /* offset= */ 1000f) + display2.addChild(display4) + + topology = DisplayTopology(display1, primaryDisplayId) + topology.normalize() + + assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) + + val actualDisplay1 = topology.root!! + verifyDisplay(actualDisplay1, id = 1, width = 200f, height = 600f, noOfChildren = 2) + + val actualDisplay2 = actualDisplay1.children[0] + verifyDisplay(actualDisplay2, id = 2, width = 600f, height = 200f, POSITION_RIGHT, + offset = 600f, noOfChildren = 1) + + val actualDisplay3 = actualDisplay1.children[1] + verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_LEFT, + offset = -200f, noOfChildren = 0) + + val actualDisplay4 = actualDisplay2.children[0] + verifyDisplay(actualDisplay4, id = 4, width = 200f, height = 600f, POSITION_TOP, + offset = 600f, noOfChildren = 0) } @Test @@ -289,34 +272,19 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) val actualDisplay1 = topology.root!! - assertThat(actualDisplay1.displayId).isEqualTo(1) - assertThat(actualDisplay1.width).isEqualTo(200f) - assertThat(actualDisplay1.height).isEqualTo(600f) - assertThat(actualDisplay1.children).hasSize(2) + verifyDisplay(actualDisplay1, id = 1, width = 200f, height = 600f, noOfChildren = 2) val actualDisplay2 = actualDisplay1.children[0] - assertThat(actualDisplay2.displayId).isEqualTo(2) - assertThat(actualDisplay2.width).isEqualTo(600f) - assertThat(actualDisplay2.height).isEqualTo(200f) - assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.offset).isEqualTo(0f) - assertThat(actualDisplay2.children).hasSize(1) + verifyDisplay(actualDisplay2, id = 2, width = 600f, height = 200f, POSITION_RIGHT, + offset = 0f, noOfChildren = 1) val actualDisplay3 = actualDisplay1.children[1] - assertThat(actualDisplay3.displayId).isEqualTo(3) - assertThat(actualDisplay3.width).isEqualTo(600f) - assertThat(actualDisplay3.height).isEqualTo(200f) - assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay3.offset).isEqualTo(400f) - assertThat(actualDisplay3.children).isEmpty() + verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_RIGHT, + offset = 400f, noOfChildren = 0) val actualDisplay4 = actualDisplay2.children[0] - assertThat(actualDisplay4.displayId).isEqualTo(4) - assertThat(actualDisplay4.width).isEqualTo(200f) - assertThat(actualDisplay4.height).isEqualTo(600f) - assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay4.offset).isEqualTo(0f) - assertThat(actualDisplay4.children).isEmpty() + verifyDisplay(actualDisplay4, id = 4, width = 200f, height = 600f, POSITION_RIGHT, + offset = 0f, noOfChildren = 0) } @Test @@ -344,34 +312,19 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) val actualDisplay1 = topology.root!! - assertThat(actualDisplay1.displayId).isEqualTo(1) - assertThat(actualDisplay1.width).isEqualTo(200f) - assertThat(actualDisplay1.height).isEqualTo(600f) - assertThat(actualDisplay1.children).hasSize(1) + verifyDisplay(actualDisplay1, id = 1, width = 200f, height = 600f, noOfChildren = 1) val actualDisplay2 = actualDisplay1.children[0] - assertThat(actualDisplay2.displayId).isEqualTo(2) - assertThat(actualDisplay2.width).isEqualTo(200f) - assertThat(actualDisplay2.height).isEqualTo(600f) - assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.offset).isEqualTo(0f) - assertThat(actualDisplay2.children).hasSize(2) + verifyDisplay(actualDisplay2, id = 2, width = 200f, height = 600f, POSITION_RIGHT, + offset = 0f, noOfChildren = 2) val actualDisplay3 = actualDisplay2.children[1] - assertThat(actualDisplay3.displayId).isEqualTo(3) - assertThat(actualDisplay3.width).isEqualTo(600f) - assertThat(actualDisplay3.height).isEqualTo(200f) - assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay3.offset).isEqualTo(10f) - assertThat(actualDisplay3.children).isEmpty() + verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_RIGHT, + offset = 10f, noOfChildren = 0) val actualDisplay4 = actualDisplay2.children[0] - assertThat(actualDisplay4.displayId).isEqualTo(4) - assertThat(actualDisplay4.width).isEqualTo(200f) - assertThat(actualDisplay4.height).isEqualTo(600f) - assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay4.offset).isEqualTo(210f) - assertThat(actualDisplay4.children).isEmpty() + verifyDisplay(actualDisplay4, id = 4, width = 200f, height = 600f, POSITION_RIGHT, + offset = 210f, noOfChildren = 0) } @Test @@ -397,26 +350,15 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) val actualDisplay1 = topology.root!! - assertThat(actualDisplay1.displayId).isEqualTo(1) - assertThat(actualDisplay1.width).isEqualTo(200f) - assertThat(actualDisplay1.height).isEqualTo(50f) - assertThat(actualDisplay1.children).hasSize(1) + verifyDisplay(actualDisplay1, id = 1, width = 200f, height = 50f, noOfChildren = 1) val actualDisplay2 = actualDisplay1.children[0] - assertThat(actualDisplay2.displayId).isEqualTo(2) - assertThat(actualDisplay2.width).isEqualTo(600f) - assertThat(actualDisplay2.height).isEqualTo(200f) - assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.offset).isEqualTo(0f) - assertThat(actualDisplay2.children).hasSize(1) + verifyDisplay(actualDisplay2, id = 2, width = 600f, height = 200f, POSITION_RIGHT, + offset = 0f, noOfChildren = 1) val actualDisplay3 = actualDisplay2.children[0] - assertThat(actualDisplay3.displayId).isEqualTo(3) - assertThat(actualDisplay3.width).isEqualTo(600f) - assertThat(actualDisplay3.height).isEqualTo(200f) - assertThat(actualDisplay3.position).isEqualTo(POSITION_BOTTOM) - assertThat(actualDisplay3.offset).isEqualTo(0f) - assertThat(actualDisplay3.children).isEmpty() + verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_BOTTOM, + offset = 0f, noOfChildren = 0) } @Test @@ -443,34 +385,19 @@ class DisplayTopologyTest { assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) val actualDisplay1 = topology.root!! - assertThat(actualDisplay1.displayId).isEqualTo(1) - assertThat(actualDisplay1.width).isEqualTo(200f) - assertThat(actualDisplay1.height).isEqualTo(600f) - assertThat(actualDisplay1.children).hasSize(1) + verifyDisplay(actualDisplay1, id = 1, width = 200f, height = 600f, noOfChildren = 1) val actualDisplay2 = actualDisplay1.children[0] - assertThat(actualDisplay2.displayId).isEqualTo(2) - assertThat(actualDisplay2.width).isEqualTo(200f) - assertThat(actualDisplay2.height).isEqualTo(600f) - assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.offset).isEqualTo(0f) - assertThat(actualDisplay2.children).hasSize(1) + verifyDisplay(actualDisplay2, id = 2, width = 200f, height = 600f, POSITION_RIGHT, + offset = 0f, noOfChildren = 1) val actualDisplay3 = actualDisplay2.children[0] - assertThat(actualDisplay3.displayId).isEqualTo(3) - assertThat(actualDisplay3.width).isEqualTo(600f) - assertThat(actualDisplay3.height).isEqualTo(200f) - assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay3.offset).isEqualTo(400f) - assertThat(actualDisplay3.children).hasSize(1) + verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_RIGHT, + offset = 400f, noOfChildren = 1) val actualDisplay4 = actualDisplay3.children[0] - assertThat(actualDisplay4.displayId).isEqualTo(4) - assertThat(actualDisplay4.width).isEqualTo(200f) - assertThat(actualDisplay4.height).isEqualTo(600f) - assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay4.offset).isEqualTo(-400f) - assertThat(actualDisplay4.children).isEmpty() + verifyDisplay(actualDisplay4, id = 4, width = 200f, height = 600f, POSITION_RIGHT, + offset = -400f, noOfChildren = 0) } @Test @@ -635,7 +562,7 @@ class DisplayTopologyTest { nodes, // In the case of corner adjacency, we prefer a left/right attachment. Pair(POSITION_RIGHT, 10f), - Pair(POSITION_BOTTOM, 40.5f), // TODO: fix implementation to remove this gap + Pair(POSITION_BOTTOM, 30f), ) assertThat(nodes[0].children).containsExactly(nodes[1]) @@ -667,34 +594,19 @@ class DisplayTopologyTest { assertThat(copy.primaryDisplayId).isEqualTo(primaryDisplayId) val actualDisplay1 = copy.root!! - assertThat(actualDisplay1.displayId).isEqualTo(1) - assertThat(actualDisplay1.width).isEqualTo(200f) - assertThat(actualDisplay1.height).isEqualTo(600f) - assertThat(actualDisplay1.children).hasSize(2) + verifyDisplay(actualDisplay1, id = 1, width = 200f, height = 600f, noOfChildren = 2) val actualDisplay2 = actualDisplay1.children[0] - assertThat(actualDisplay2.displayId).isEqualTo(2) - assertThat(actualDisplay2.width).isEqualTo(600f) - assertThat(actualDisplay2.height).isEqualTo(200f) - assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.offset).isEqualTo(0f) - assertThat(actualDisplay2.children).hasSize(1) + verifyDisplay(actualDisplay2, id = 2, width = 600f, height = 200f, POSITION_RIGHT, + offset = 0f, noOfChildren = 1) val actualDisplay3 = actualDisplay1.children[1] - assertThat(actualDisplay3.displayId).isEqualTo(3) - assertThat(actualDisplay3.width).isEqualTo(600f) - assertThat(actualDisplay3.height).isEqualTo(200f) - assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay3.offset).isEqualTo(400f) - assertThat(actualDisplay3.children).isEmpty() + verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_RIGHT, + offset = 400f, noOfChildren = 0) val actualDisplay4 = actualDisplay2.children[0] - assertThat(actualDisplay4.displayId).isEqualTo(4) - assertThat(actualDisplay4.width).isEqualTo(200f) - assertThat(actualDisplay4.height).isEqualTo(600f) - assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay4.offset).isEqualTo(0f) - assertThat(actualDisplay4.children).isEmpty() + verifyDisplay(actualDisplay4, id = 4, width = 200f, height = 600f, POSITION_RIGHT, + offset = 0f, noOfChildren = 0) } /** @@ -722,9 +634,20 @@ class DisplayTopologyTest { return nodes } + private fun verifyDisplay(display: DisplayTopology.TreeNode, id: Int, width: Float, + height: Float, @DisplayTopology.TreeNode.Position position: Int = 0, + offset: Float = 0f, noOfChildren: Int) { + assertThat(display.displayId).isEqualTo(id) + assertThat(display.width).isEqualTo(width) + assertThat(display.height).isEqualTo(height) + assertThat(display.position).isEqualTo(position) + assertThat(display.offset).isEqualTo(offset) + assertThat(display.children).hasSize(noOfChildren) + } + private fun assertPositioning( nodes: List<DisplayTopology.TreeNode>, vararg positions: Pair<Int, Float>) { - assertThat(nodes.drop(1).map { Pair(it.position, it.offset )}) + assertThat(nodes.drop(1).map { Pair(it.position, it.offset) }) .containsExactly(*positions) .inOrder() } diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java index 55b292aefec4..5b78726cc421 100644 --- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java @@ -100,13 +100,14 @@ class DisplayTopologyCoordinator { */ DisplayTopology getTopology() { synchronized (mSyncRoot) { - return mTopology; + return mTopology.copy(); } } void setTopology(DisplayTopology topology) { synchronized (mSyncRoot) { mTopology = topology; + mTopology.normalize(); sendTopologyUpdateLocked(); } } 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 e4b461f307d3..5d427139a857 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt @@ -20,6 +20,7 @@ import android.hardware.display.DisplayTopology import android.util.DisplayMetrics 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.ArgumentMatchers.anyFloat @@ -87,4 +88,21 @@ class DisplayTopologyCoordinatorTest { verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat()) verify(mockTopologyChangedCallback, never()).invoke(any()) } + + @Test + fun getTopology_copy() { + assertThat(coordinator.topology).isEqualTo(mockTopologyCopy) + } + + @Test + fun setTopology_normalize() { + val topology = mock<DisplayTopology>() + val topologyCopy = mock<DisplayTopology>() + whenever(topology.copy()).thenReturn(topologyCopy) + + coordinator.topology = topology + + verify(topology).normalize() + verify(mockTopologyChangedCallback).invoke(topologyCopy) + } }
\ No newline at end of file |