From 047bc0ce6cae49415240fcce8ccdd2886248faab Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Wed, 8 Mar 2023 16:24:34 -0800 Subject: [PATCH] Allow the use of input features for windowless windows In particular spy windows are specified in input features. Windowless windows are more lightweight than regular windows in implementing spy windows. It also helps overlays (e.g. window decorations) to pass input events to app content, and only consume them when necessary. Bug: 266866903 Test: Input of window decorations still work. Test: atest WmTests:WindowManagerServiceTests Change-Id: Ifbd2bc1a5966325b5bc28e58a19fcd3e05aafdf2 --- core/java/android/view/IWindowSession.aidl | 5 +- .../android/view/WindowlessWindowManager.java | 28 +++-- .../windowdecor/DragResizeInputListener.java | 2 + .../java/com/android/server/wm/Session.java | 8 +- .../server/wm/WindowManagerService.java | 16 +-- .../server/wm/WindowManagerServiceTests.java | 105 ++++++++++++++++++ 6 files changed, 139 insertions(+), 25 deletions(-) diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 5810642402a31..bc0bf8c41d3ce 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -304,7 +304,7 @@ interface IWindowSession { * an input channel where the client can receive input. */ void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window, - in IBinder hostInputToken, int flags, int privateFlags, int type, + in IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type, in IBinder windowToken, in IBinder focusGrantToken, String inputHandleName, out InputChannel outInputChannel); @@ -312,7 +312,8 @@ interface IWindowSession { * Update the flags on an input channel associated with a particular surface. */ oneway void updateInputChannel(in IBinder channelToken, int displayId, - in SurfaceControl surface, int flags, int privateFlags, in Region region); + in SurfaceControl surface, int flags, int privateFlags, int inputFeatures, + in Region region); /** * Transfer window focus to an embedded window if the calling window has focus. diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index b157ea0c641fb..9e998cec2dbf9 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -139,7 +139,7 @@ public class WindowlessWindowManager implements IWindowSession { try { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, state.mSurfaceControl, state.mParams.flags, state.mParams.privateFlags, - state.mInputRegion); + state.mParams.inputFeatures, state.mInputRegion); } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); } @@ -189,12 +189,13 @@ public class WindowlessWindowManager implements IWindowSession { mRealWm.grantInputChannel(displayId, new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"), window, mHostInputToken, - attrs.flags, attrs.privateFlags, attrs.type, attrs.token, - mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); + attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, + attrs.token, mFocusGrantToken, attrs.getTitle().toString(), + outInputChannel); } else { mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, - attrs.privateFlags, attrs.type, attrs.token, mFocusGrantToken, - attrs.getTitle().toString(), outInputChannel); + attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token, + mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); } } catch (RemoteException e) { Log.e(TAG, "Failed to grant input to surface: ", e); @@ -381,16 +382,19 @@ public class WindowlessWindowManager implements IWindowSession { outMergedConfiguration.setConfiguration(mConfiguration, mConfiguration); } - if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0 - && state.mInputChannelToken != null) { + final int inputChangeMask = WindowManager.LayoutParams.FLAGS_CHANGED + | WindowManager.LayoutParams.INPUT_FEATURES_CHANGED; + if ((attrChanges & inputChangeMask) != 0 && state.mInputChannelToken != null) { try { - if(mRealWm instanceof IWindowSession.Stub) { + if (mRealWm instanceof IWindowSession.Stub) { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, new SurfaceControl(sc, "WindowlessWindowManager.relayout"), - attrs.flags, attrs.privateFlags, state.mInputRegion); + attrs.flags, attrs.privateFlags, attrs.inputFeatures, + state.mInputRegion); } else { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc, - attrs.flags, attrs.privateFlags, state.mInputRegion); + attrs.flags, attrs.privateFlags, attrs.inputFeatures, + state.mInputRegion); } } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); @@ -564,14 +568,14 @@ public class WindowlessWindowManager implements IWindowSession { @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, - IBinder hostInputToken, int flags, int privateFlags, int type, + IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken, IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { } @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, int privateFlags, Region region) { + int flags, int privateFlags, int inputFeatures, Region region) { } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 0a9c3310a883d..8cb575cc96e33 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -103,6 +103,7 @@ class DragResizeInputListener implements AutoCloseable { null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, mFocusGrantToken, @@ -208,6 +209,7 @@ class DragResizeInputListener implements AutoCloseable { mDecorationSurface, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, touchRegion); } catch (RemoteException e) { e.rethrowFromSystemServer(); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index ce9bff8521e63..75961f74a3c46 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -862,7 +862,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, - IBinder windowToken, IBinder focusGrantToken, String inputHandleName, + int inputFeatures, IBinder windowToken, IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { if (hostInputToken == null && !mCanAddInternalSystemWindow) { // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to @@ -874,7 +874,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { try { mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0, - type, windowToken, focusGrantToken, inputHandleName, + type, inputFeatures, windowToken, focusGrantToken, inputHandleName, outInputChannel); } finally { Binder.restoreCallingIdentity(identity); @@ -883,11 +883,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, int privateFlags, Region region) { + int flags, int privateFlags, int inputFeatures, Region region) { final long identity = Binder.clearCallingIdentity(); try { mService.updateInputChannel(channelToken, displayId, surface, flags, - mCanAddInternalSystemWindow ? privateFlags : 0, region); + mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, 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 98563f6e73fca..05aed30b1f080 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8753,8 +8753,8 @@ public class WindowManagerService extends IWindowManager.Stub */ void grantInputChannel(Session session, int callingUid, int callingPid, int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, - int flags, int privateFlags, int type, IBinder windowToken, IBinder focusGrantToken, - String inputHandleName, InputChannel outInputChannel) { + int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken, + IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type); final InputApplicationHandle applicationHandle; final String name; @@ -8771,7 +8771,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, - name, applicationHandle, flags, privateFlags, sanitizedType, + name, applicationHandle, flags, privateFlags, inputFeatures, sanitizedType, null /* region */, window); clientChannel.copyTo(outInputChannel); @@ -8812,13 +8812,14 @@ 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, - int privateFlags, int type, Region region, IWindow window) { + int privateFlags, int inputFeatures, int type, Region region, IWindow window) { final InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; h.setWindowToken(window); h.name = name; flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid); + inputFeatures = sanitizeSpyWindow(inputFeatures, name, callingUid, callingPid); final int sanitizedLpFlags = (flags & (FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE)) @@ -8828,7 +8829,7 @@ public class WindowManagerService extends IWindowManager.Stub // Do not allow any input features to be set without sanitizing them first. h.inputConfig = InputConfigAdapter.getInputConfigFromWindowParams( - type, sanitizedLpFlags, 0 /*inputFeatures*/); + type, sanitizedLpFlags, inputFeatures); if ((flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0) { @@ -8865,7 +8866,7 @@ public class WindowManagerService extends IWindowManager.Stub * is undefined. */ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, int privateFlags, Region region) { + int flags, int privateFlags, int inputFeatures, Region region) { final InputApplicationHandle applicationHandle; final String name; final EmbeddedWindowController.EmbeddedWindow win; @@ -8880,7 +8881,8 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, - applicationHandle, flags, privateFlags, win.mWindowType, region, win.mClient); + applicationHandle, flags, privateFlags, inputFeatures, win.mWindowType, region, + win.mClient); } /** Return whether layer tracing is enabled */ diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 7d6cf4a63c43d..677ec46007ff1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -26,7 +26,10 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_OWN_FOCUS; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -49,11 +52,13 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -67,12 +72,16 @@ import android.hardware.display.VirtualDisplay; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.InputConfig; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.util.MergedConfiguration; +import android.view.IWindow; import android.view.IWindowSessionCallback; +import android.view.InputChannel; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.Surface; @@ -690,6 +699,102 @@ public class WindowManagerServiceTests extends WindowTestsBase { assertEquals(validRect, resultingArgs.mSourceCrop); } + @Test + public void testGrantInputChannel_sanitizeSpyWindowForApplications() { + final Session session = mock(Session.class); + final int callingUid = Process.FIRST_APPLICATION_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + assertThrows(IllegalArgumentException.class, () -> + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, + surfaceControl, window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, + PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, TYPE_APPLICATION, + null /* windowToken */, focusGrantToken, "TestInputChannel", + inputChannel)); + } + + @Test + public void testGrantInputChannel_allowSpyWindowForInputMonitorPermission() { + final Session session = mock(Session.class); + final int callingUid = Process.SYSTEM_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl, + window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + INPUT_FEATURE_SPY, TYPE_APPLICATION, null /* windowToken */, focusGrantToken, + "TestInputChannel", inputChannel); + + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY)); + } + + @Test + public void testUpdateInputChannel_sanitizeSpyWindowForApplications() { + final Session session = mock(Session.class); + final int callingUid = Process.FIRST_APPLICATION_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl, + window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken, + "TestInputChannel", inputChannel); + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == 0)); + + assertThrows(IllegalArgumentException.class, () -> + mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl, + FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, + null /* region */)); + } + + @Test + public void testUpdateInputChannel_allowSpyWindowForInputMonitorPermission() { + final Session session = mock(Session.class); + final int callingUid = Process.SYSTEM_UID; + final int callingPid = 1234; + final SurfaceControl surfaceControl = mock(SurfaceControl.class); + final IWindow window = mock(IWindow.class); + final IBinder windowToken = mock(IBinder.class); + when(window.asBinder()).thenReturn(windowToken); + final IBinder focusGrantToken = mock(IBinder.class); + + final InputChannel inputChannel = new InputChannel(); + mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl, + window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, + 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken, + "TestInputChannel", inputChannel); + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == 0)); + + mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl, + FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, + null /* region */); + verify(mTransaction).setInputWindowInfo( + eq(surfaceControl), + argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY)); + } + private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) { final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class); when(remoteToken.toWindowContainerToken()).thenReturn(wct);