From 128372810ddc47891b5cb93f6a61844bb474e425 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Mon, 27 Apr 2020 19:12:25 -0700 Subject: [PATCH] Give Divider back its touch-region Divider used to have a touch-region that allowed apps to receive touch events that would normally overlap the divider window as long as they weren't in the center. Re-introduce this by allowing windowless windows to pass through a touch-region. This region is always constrained to the window size to prevent security issues. Bug: 154840035 Test: Enter split and observe touch events away from center of divider. Change-Id: I85310c78beda7bd5574df78e2a83462e965d1154 --- core/java/android/view/IWindowSession.aidl | 2 +- .../android/view/SurfaceControlViewHost.java | 8 +++++ .../android/view/WindowlessWindowManager.java | 32 +++++++++++++++++-- .../systemui/stackdivider/DividerView.java | 17 +++++++++- .../stackdivider/DividerWindowManager.java | 9 ++++++ .../android/systemui/wm/SystemWindows.java | 27 ++++++++++++++++ .../java/com/android/server/wm/Session.java | 4 +-- .../server/wm/WindowManagerService.java | 16 +++++++--- 8 files changed, 104 insertions(+), 11 deletions(-) diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 25e81114d86f2..926d8fc5a368c 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -342,5 +342,5 @@ interface IWindowSession { * Update the flags on an input channel associated with a particular surface. */ void updateInputChannel(in IBinder channelToken, int displayId, in SurfaceControl surface, - int flags); + int flags, in Region region); } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 3d6da6f71b3ff..7ec008c901bc1 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -233,6 +233,14 @@ public class SurfaceControlViewHost { return mViewRoot.mWindow; } + /** + * @return the WindowlessWindowManager instance that this host is attached to. + * @hide + */ + public @NonNull WindowlessWindowManager getWindowlessWM() { + return mWm; + } + /** * @hide */ diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 397bce44b25e4..95d6d651cc792 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -16,16 +16,19 @@ package android.view; +import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.util.MergedConfiguration; import java.util.HashMap; +import java.util.Objects; /** * A simplistic implementation of IWindowSession. Rather than managing Surfaces @@ -42,6 +45,7 @@ public class WindowlessWindowManager implements IWindowSession { WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); int mDisplayId; IBinder mInputChannelToken; + Region mInputRegion; State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IBinder inputChannelToken) { mSurfaceControl = sc; @@ -95,6 +99,30 @@ public class WindowlessWindowManager implements IWindowSession { mResizeCompletionForWindow.put(window, callback); } + protected void setTouchRegion(IBinder window, @Nullable Region region) { + State state; + synchronized (this) { + // Do everything while locked so that we synchronize with relayout. This should be a + // very infrequent operation. + state = mStateForWindow.get(window); + if (state == null) { + return; + } + if (Objects.equals(region, state.mInputRegion)) { + return; + } + state.mInputRegion = region != null ? new Region(region) : null; + if (state.mInputChannelToken != null) { + try { + mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, + state.mSurfaceControl, state.mParams.flags, state.mInputRegion); + } catch (RemoteException e) { + Log.e(TAG, "Failed to update surface input channel: ", e); + } + } + } + } + /** * IWindowSession implementation. */ @@ -234,7 +262,7 @@ public class WindowlessWindowManager implements IWindowSession { && state.mInputChannelToken != null) { try { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc, - attrs.flags); + attrs.flags, state.mInputRegion); } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); } @@ -409,7 +437,7 @@ public class WindowlessWindowManager implements IWindowSession { @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags) { + int flags, Region region) { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 2df450604d3b7..060760a2f9400 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -29,6 +29,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; +import android.graphics.Region; import android.graphics.Region.Op; import android.hardware.display.DisplayManager; import android.os.Bundle; @@ -338,12 +339,12 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); if (mFirstLayout) { // Wait for first layout so that the ViewRootImpl surface has been created. initializeSurfaceState(); mFirstLayout = false; } - super.onLayout(changed, left, top, right, bottom); int minimizeLeft = 0; int minimizeTop = 0; if (mDockSide == WindowManager.DOCKED_TOP) { @@ -783,6 +784,20 @@ public class DividerView extends FrameLayout implements OnTouchListener, setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */); t.apply(); mTiles.releaseTransaction(t); + + // Get the actually-visible bar dimensions (relative to full window). This is a thin + // bar going through the center. + final Rect dividerBar = isHorizontalDivision() + ? new Rect(0, mDividerInsets, mSplitLayout.mDisplayLayout.width(), + mDividerInsets + mDividerSize) + : new Rect(mDividerInsets, 0, mDividerInsets + mDividerSize, + mSplitLayout.mDisplayLayout.height()); + final Region touchRegion = new Region(dividerBar); + // Add in the "draggable" portion. While not visible, this is an expanded area that the + // user can interact with. + touchRegion.union(new Rect(mHandle.getLeft(), mHandle.getTop(), + mHandle.getRight(), mHandle.getBottom())); + mWindowManager.setTouchRegion(touchRegion); } public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java index 729df3887915c..6ea3132ac942f 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import android.graphics.PixelFormat; +import android.graphics.Region; import android.os.Binder; import android.view.View; import android.view.WindowManager; @@ -103,4 +104,12 @@ public class DividerWindowManager { mSystemWindows.updateViewLayout(mView, mLp); } } + + /** Sets the touch region to `touchRegion`. Use null to unset.*/ + public void setTouchRegion(Region touchRegion) { + if (mView == null) { + return; + } + mSystemWindows.setTouchableRegion(mView, touchRegion); + } } diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java index 0b6e4b2ab5980..64cac84ff24e1 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java +++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java @@ -23,7 +23,9 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.os.Bundle; +import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; @@ -138,6 +140,23 @@ public class SystemWindows { root.relayout((WindowManager.LayoutParams) params); } + /** + * Sets the touchable region of a view's window. This will be cropped to the window size. + * @param view + * @param region + */ + public void setTouchableRegion(@NonNull View view, Region region) { + SurfaceControlViewHost root = mViewRoots.get(view); + if (root == null) { + return; + } + WindowlessWindowManager wwm = root.getWindowlessWM(); + if (!(wwm instanceof SysUiWindowManager)) { + return; + } + ((SysUiWindowManager) wwm).setTouchableRegionForWindow(view, region); + } + /** * Adds a root for system-ui window management with no views. Only useful for IME. */ @@ -289,6 +308,14 @@ public class SystemWindows { SurfaceControl getSurfaceControlForWindow(View rootView) { return getSurfaceControl(rootView); } + + void setTouchableRegionForWindow(View rootView, Region region) { + IBinder token = rootView.getWindowToken(); + if (token == null) { + return; + } + setTouchRegion(token, region); + } } class ContainerWindow extends IWindow.Stub { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 56147f216e73e..bf20cb907b712 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -679,10 +679,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags) { + int flags, Region region) { final long identity = Binder.clearCallingIdentity(); try { - mService.updateInputChannel(channelToken, displayId, surface, flags); + mService.updateInputChannel(channelToken, displayId, surface, flags, region); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 84d749f148fb2..9c6e779dfa494 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8023,7 +8023,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, - name, applicationHandle, flags); + name, applicationHandle, flags, null /* region */); clientChannel.transferTo(outInputChannel); clientChannel.dispose(); @@ -8035,7 +8035,7 @@ public class WindowManagerService extends IWindowManager.Stub private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, int displayId, SurfaceControl surface, String name, - InputApplicationHandle applicationHandle, int flags) { + InputApplicationHandle applicationHandle, int flags, Region region) { InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; h.name = name; @@ -8055,7 +8055,13 @@ public class WindowManagerService extends IWindowManager.Stub h.inputFeatures = 0; - h.replaceTouchableRegionWithCrop(null); + if (region == null) { + h.replaceTouchableRegionWithCrop(null); + } else { + h.touchableRegion.set(region); + h.replaceTouchableRegionWithCrop = false; + h.setTouchableRegionCrop(surface); + } SurfaceControl.Transaction t = mTransactionFactory.get(); t.setInputWindowInfo(surface, h); @@ -8069,7 +8075,7 @@ public class WindowManagerService extends IWindowManager.Stub * is undefined. */ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags) { + int flags, Region region) { final InputApplicationHandle applicationHandle; final String name; final EmbeddedWindowController.EmbeddedWindow win; @@ -8084,7 +8090,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, - applicationHandle, flags); + applicationHandle, flags, region); } /** Return whether layer tracing is enabled */