diff options
| author | 2019-04-18 14:35:09 -0700 | |
|---|---|---|
| committer | 2019-05-02 12:53:55 +0800 | |
| commit | d8ec93860957c95d00b1c1fa18ba2f302d06720e (patch) | |
| tree | b494ba05fc7531b98a1c1cf857ee2a83418b1873 | |
| parent | 81894407855a3a4769b966f857ea867092c358ac (diff) | |
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
9 files changed, 242 insertions, 68 deletions
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index fc6fffabc917..b64b2dcc4f61 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -317,7 +317,7 @@ public class ActivityView extends ViewGroup { * regions and avoid focus switches by touches on this view. */ public void onLocationChanged() { - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -329,39 +329,52 @@ public class ActivityView extends ViewGroup { public boolean gatherTransparentRegion(Region region) { // The tap exclude region may be affected by any view on top of it, so we detect the // possible change by monitoring this function. - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); return super.gatherTransparentRegion(region); } - /** Compute and send current tap exclude region to WM for this view. */ - private void updateTapExcludeRegion() { - if (!isAttachedToWindow()) { - return; - } - if (!canReceivePointerEvents()) { - cleanTapExcludeRegion(); + /** + * Sends current location in window and tap exclude region to WM for this view. + */ + private void updateLocationAndTapExcludeRegion() { + if (mVirtualDisplay == null || !isAttachedToWindow()) { return; } try { + int x = mLocationInWindow[0]; + int y = mLocationInWindow[1]; getLocationInWindow(mLocationInWindow); - final int x = mLocationInWindow[0]; - final int y = mLocationInWindow[1]; - mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); - - // There might be views on top of us. We need to subtract those areas from the tap - // exclude region. - final ViewParent parent = getParent(); - if (parent instanceof ViewGroup) { - ((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this); + if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) { + x = mLocationInWindow[0]; + y = mLocationInWindow[1]; + WindowManagerGlobal.getWindowSession().updateDisplayContentLocation( + getWindow(), x, y, mVirtualDisplay.getDisplay().getDisplayId()); } - - WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - mTapExcludeRegion); + updateTapExcludeRegion(x, y); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } + /** Computes and sends current tap exclude region to WM for this view. */ + private void updateTapExcludeRegion(int x, int y) throws RemoteException { + if (!canReceivePointerEvents()) { + cleanTapExcludeRegion(); + return; + } + mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); + + // There might be views on top of us. We need to subtract those areas from the tap + // exclude region. + final ViewParent parent = getParent(); + if (parent != null) { + parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this); + } + + WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), + mTapExcludeRegion); + } + private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { @@ -379,7 +392,7 @@ public class ActivityView extends ViewGroup { mVirtualDisplay.setDisplayState(true); } - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -387,7 +400,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -471,7 +484,8 @@ public class ActivityView extends ViewGroup { try { // TODO: Find a way to consolidate these calls to the server. - wm.reparentDisplayContent(displayId, mRootSurfaceControl); + WindowManagerGlobal.getWindowSession().reparentDisplayContent( + getWindow(), mRootSurfaceControl, displayId); wm.dontOverrideDisplayInfo(displayId); if (mSingleTaskInstance) { mActivityTaskManager.setDisplayToSingleTaskInstance(displayId); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c730fe2dc114..b347a78a8780 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -621,18 +621,6 @@ interface IWindowManager */ void setShouldShowIme(int displayId, boolean shouldShow); - /** - * Reparent the top layers for a display to the requested surfaceControl. The display that - * is going to be re-parented (the displayId passed in) needs to have been created by the same - * process that is requesting the re-parent. This is to ensure clients can't just re-parent - * display content info to any SurfaceControl, as this would be a security issue. - * - * @param displayId The id of the display. - * @param surfaceControlHandle The SurfaceControl that the top level layers for the - * display should be re-parented to. - */ - void reparentDisplayContent(int displayId, in SurfaceControl sc); - /** * Waits for transactions to get applied before injecting input. * This includes waiting for the input windows to get sent to InputManager. diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index b52fdb8399d1..d269323d50a4 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -257,6 +257,31 @@ interface IWindowSession { void updatePointerIcon(IWindow window); /** + * Reparent the top layers for a display to the requested SurfaceControl. The display that is + * going to be re-parented (the displayId passed in) needs to have been created by the same + * process that is requesting the re-parent. This is to ensure clients can't just re-parent + * display content info to any SurfaceControl, as this would be a security issue. + * + * @param window 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 SurfaceControl that the top level layers for the display should be re-parented + * to. + * @param displayId The id of the display to be re-parented. + */ + void reparentDisplayContent(IWindow window, in SurfaceControl sc, int displayId); + + /** + * Update the location of a child display in its parent window. This enables windows in the + * child display to compute the global transformation matrix. + * + * @param window The parent window of the display. + * @param x The x coordinate in the parent window. + * @param y The y coordinate in the parent window. + * @param displayId The id of the display to be notified. + */ + void updateDisplayContentLocation(IWindow window, int x, int y, int displayId); + + /** * Update a tap exclude region identified by provided id in the window. Touches on this region * will neither be dispatched to this window nor change the focus to this window. Passing an * invalid region will remove the area from the exclude region of this window. diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 6ce42ec4cb44..b6a5be807fb6 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 @@ final class AccessibilityController { // 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 @@ final class AccessibilityController { 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 3f783f67b73c..b4b60ea4bb6f 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.content.res.Configuration; 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final InsetsStateController mInsetsStateController; + /** @see #getParentWindow() */ + private WindowState mParentWindow; + + private Point mLocationInParentWindow = new Point(); private SurfaceControl mParentSurfaceControl; private InputWindowHandle mPortalWindowHandle; @@ -4923,11 +4928,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo .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 b33f8c7ad658..34273f3f23a5 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 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @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 4f849cde8948..7c1ea208e270 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 @@ public class WindowManagerService extends IWindowManager.Stub // 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 @@ public class WindowManagerService extends IWindowManager.Stub } 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 @@ public class WindowManagerService extends IWindowManager.Stub } } + 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 @@ public class WindowManagerService extends IWindowManager.Stub } @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 8123182a3e4b..2a621be8071a 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 98d055c78e3c..3a8d3b74c08f 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 @@ public class WindowStateTests extends WindowTestsBase { 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); + } + } } |