summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Piotr WilczyƄski <wilczynskip@google.com> 2024-11-29 14:00:34 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-29 14:00:34 +0000
commitf2290c21dbd263254e85e46a1467d73cc69b5aa5 (patch)
tree47d0f97f6b2cc370efc644a5a15df879a60172e5
parented7d191a5974225d59b62e4c0efd92ca6a33d3a8 (diff)
parent060d32262d56358d7bf0c5c98c423a4c599d5cf4 (diff)
Merge "Get topology copy, set topology normalize" into main
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java298
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt289
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt18
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