Refine getTransformationMatrix for windows in a re-parented display
Currently, the translation of the transformation matrix computed by
WindowState.getTransformationMatrix is related to its own display.
However, if the display has been re-parented, the translation might
be misplaced to the visual result. This CL makes it return the global
transformation matrix.
Bug: 129098348
Test: atest WindowStateTests
Change-Id: I38da5b84a11890bf0f4a57eb9d5b7e71bdcc16a9
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6ce42ec..b6a5be8 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1108,6 +1108,7 @@
// the window manager is still looking for where to put it.
// We will do the work when we get a focus change callback.
// TODO(b/112273690): Support multiple displays
+ // TODO(b/129098348): Support embedded displays
if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) {
return;
}
@@ -1400,7 +1401,28 @@
if (w.isVisibleLw()) {
outWindows.put(mTempLayer++, w);
}
- }, false /* traverseTopToBottom */ );
+ }, false /* traverseTopToBottom */);
+ mService.mRoot.forAllWindows(w -> {
+ final WindowState win = findRootDisplayParentWindow(w);
+ if (win != null && win.getDisplayContent().isDefaultDisplay && w.isVisibleLw()) {
+ // TODO(b/129098348): insert windows on child displays into outWindows based on
+ // root-display-parent window.
+ outWindows.put(mTempLayer++, w);
+ }
+ }, false /* traverseTopToBottom */);
+ }
+
+ private WindowState findRootDisplayParentWindow(WindowState win) {
+ WindowState displayParentWindow = win.getDisplayContent().getParentWindow();
+ if (displayParentWindow == null) {
+ return null;
+ }
+ WindowState candidate = displayParentWindow;
+ while (candidate != null) {
+ displayParentWindow = candidate;
+ candidate = displayParentWindow.getDisplayContent().getParentWindow();
+ }
+ return displayParentWindow;
}
private class MyHandler extends Handler {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3f783f6..b4b60ea 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -144,6 +144,7 @@
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
@@ -541,6 +542,10 @@
private final InsetsStateController mInsetsStateController;
+ /** @see #getParentWindow() */
+ private WindowState mParentWindow;
+
+ private Point mLocationInParentWindow = new Point();
private SurfaceControl mParentSurfaceControl;
private InputWindowHandle mPortalWindowHandle;
@@ -4923,11 +4928,14 @@
/**
* Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
- * {@link #mOverlayLayer} to the specified surfaceControl.
+ * {@link #mOverlayLayer} to the specified SurfaceControl.
*
+ * @param win The window which owns the SurfaceControl. This indicates the z-order of the
+ * windows of this display against the windows on the parent display.
* @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to.
*/
- void reparentDisplayContent(SurfaceControl sc) {
+ void reparentDisplayContent(WindowState win, SurfaceControl sc) {
+ mParentWindow = win;
mParentSurfaceControl = sc;
if (mPortalWindowHandle == null) {
mPortalWindowHandle = createPortalWindowHandle(sc.toString());
@@ -4936,6 +4944,41 @@
.reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
}
+ /**
+ * Get the window which owns the surface that this DisplayContent is re-parented to.
+ *
+ * @return the parent window.
+ */
+ WindowState getParentWindow() {
+ return mParentWindow;
+ }
+
+ /**
+ * Update the location of this display in the parent window. This enables windows in this
+ * display to compute the global transformation matrix.
+ *
+ * @param win The parent window of this display.
+ * @param x The x coordinate in the parent window.
+ * @param y The y coordinate in the parent window.
+ */
+ void updateLocation(WindowState win, int x, int y) {
+ if (mParentWindow != win) {
+ throw new IllegalArgumentException(
+ "The given window is not the parent window of this display.");
+ }
+ if (mLocationInParentWindow.x != x || mLocationInParentWindow.y != y) {
+ mLocationInParentWindow.x = x;
+ mLocationInParentWindow.y = y;
+ if (mWmService.mAccessibilityController != null) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+ }
+ }
+
+ Point getLocationInParentWindow() {
+ return mLocationInParentWindow;
+ }
+
@VisibleForTesting
SurfaceControl getWindowingLayer() {
return mWindowingLayer;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b33f8c7..34273f3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -426,6 +426,16 @@
}
@Override
+ public void reparentDisplayContent(IWindow window, SurfaceControl sc, int displayId) {
+ mService.reparentDisplayContent(window, sc, displayId);
+ }
+
+ @Override
+ public void updateDisplayContentLocation(IWindow window, int x, int y, int displayId) {
+ mService.updateDisplayContentLocation(window, x, y, displayId);
+ }
+
+ @Override
public void updateTapExcludeRegion(IWindow window, int regionId, Region region) {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4f849cd..7c1ea20 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1880,7 +1880,8 @@
// We need to report touchable region changes to accessibility.
if (mAccessibilityController != null
- && w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
+ && (w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY
+ || w.getDisplayContent().getParentWindow() != null)) {
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
}
@@ -2012,7 +2013,8 @@
}
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
&& (mAccessibilityController != null)
- && (win.getDisplayId() == DEFAULT_DISPLAY)) {
+ && (win.getDisplayId() == DEFAULT_DISPLAY
+ || win.getDisplayContent().getParentWindow() != null)) {
// No move or resize, but the controller checks for title changes as well
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -6703,6 +6705,61 @@
}
}
+ private void checkCallerOwnsDisplay(int displayId) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException(
+ "Cannot find display for non-existent displayId: " + displayId);
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int displayOwnerUid = display.getOwnerUid();
+ if (callingUid != displayOwnerUid) {
+ throw new SecurityException("The caller doesn't own the display.");
+ }
+ }
+
+ /** @see Session#reparentDisplayContent(IWindow, SurfaceControl, int) */
+ void reparentDisplayContent(IWindow client, SurfaceControl sc, int displayId) {
+ checkCallerOwnsDisplay(displayId);
+
+ synchronized (mGlobalLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final WindowState win = windowForClientLocked(null, client, false);
+ if (win == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + client);
+ return;
+ }
+ getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ /** @see Session#updateDisplayContentLocation(IWindow, int, int, int) */
+ void updateDisplayContentLocation(IWindow client, int x, int y, int displayId) {
+ checkCallerOwnsDisplay(displayId);
+
+ synchronized (mGlobalLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final WindowState win = windowForClientLocked(null, client, false);
+ if (win == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + client);
+ return;
+ }
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.updateLocation(win, x, y);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
/**
* Update a tap exclude region in the window identified by the provided id. Touches down on this
* region will not:
@@ -7538,31 +7595,6 @@
}
@Override
- public void reparentDisplayContent(int displayId, SurfaceControl sc) {
- final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null) {
- throw new IllegalArgumentException(
- "Can't reparent display for non-existent displayId: " + displayId);
- }
-
- final int callingUid = Binder.getCallingUid();
- final int displayOwnerUid = display.getOwnerUid();
- if (callingUid != displayOwnerUid) {
- throw new SecurityException("Only owner of the display can reparent surfaces to it.");
- }
-
- synchronized (mGlobalLock) {
- long token = Binder.clearCallingIdentity();
- try {
- DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
- displayContent.reparentDisplayContent(sc);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
- @Override
public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
boolean shouldWaitForAnimToComplete = false;
if (ev instanceof KeyEvent) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8123182..2a621be 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3142,7 +3142,8 @@
}
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
+ || getDisplayContent().getParentWindow() != null)) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -4207,7 +4208,8 @@
}
//TODO (multidisplay): Accessibility is supported only for the default display.
- if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
+ || getDisplayContent().getParentWindow() != null)) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -4646,6 +4648,18 @@
int x = mSurfacePosition.x;
int y = mSurfacePosition.y;
+ // We might be on a display which has been re-parented to a view in another window, so here
+ // computes the global location of our display.
+ DisplayContent dc = getDisplayContent();
+ while (dc != null && dc.getParentWindow() != null) {
+ final WindowState displayParent = dc.getParentWindow();
+ x += displayParent.mWindowFrames.mFrame.left - displayParent.mAttrs.surfaceInsets.left
+ + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f);
+ y += displayParent.mWindowFrames.mFrame.top - displayParent.mAttrs.surfaceInsets.top
+ + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f);
+ dc = displayParent.getDisplayContent();
+ }
+
// If changed, also adjust transformFrameToSurfacePosition
final WindowContainer parent = getParent();
if (isChildWindow()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 98d055c..3a8d3b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -493,4 +493,30 @@
assertTrue(window.isVisible());
assertTrue(window.isVisibleByPolicy());
}
+
+ @Test
+ public void testGetTransformationMatrix() {
+ synchronized (mWm.mGlobalLock) {
+ final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+ win0.getFrameLw().offsetTo(1, 0);
+
+ final DisplayContent dc = createNewDisplay();
+ dc.reparentDisplayContent(win0, win0.getSurfaceControl());
+ dc.updateLocation(win0, 2, 0);
+
+ final float[] values = new float[9];
+ final Matrix matrix = new Matrix();
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
+ win1.mHasSurface = true;
+ win1.mSurfaceControl = mock(SurfaceControl.class);
+ win1.getFrameLw().offsetTo(3, 0);
+ win1.updateSurfacePosition(t);
+ win1.getTransformationMatrix(values, matrix);
+
+ matrix.getValues(values);
+ assertEquals(6f, values[Matrix.MTRANS_X], 0f);
+ assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
+ }
+ }
}