From 3213c9657b88e9d2d0d6f529e1a63bb30ff42cdc Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 30 Oct 2020 15:07:28 +0800 Subject: [PATCH] Reduce unnecessary invocation of setInputWindowInfo Usually most fields of InputWindowHandle don't change frequently. Therefore, only the changed instances need to be updated. That reduces the overhead of JNI invocation (especially NativeInputWindowHandle::updateInfo which may be called from setInputWindowInfo). There should be no behavior change. - Add a InputWindowHandle.ChangeDetectionWrapper to wrap the original handle. So the changes of its fields can be tracked. - Make InputApplicationHandle java side immutable. Its content should be rarely changed. Then it is easier to compare by instance. This might also reduces the race condition of accessing its field from InputDispatcher because the instance is different. - Move some fields that won't change of InputWindowHandle to the constructor of WindowState to reduce unnecessary updates. - When a window cannot receive input, reuse the per-window input window handle to populate the disabled info, so there won't be a shared instance that its fields always need to be updated. - Reduce unnecessary Region#translate if the offsets are zero. - For a simple activity launch, the invocation amount of setInputWindowInfo is reduced 90% (from 126 to 11). - The metrics updateInputWindows_mean of WmPerfTests is reduced 50%+ (from 0.89ms to 0.38ms on an old mid-end device). Bug: 168008622 Test: WindowStateTests#testUpdateInputWindowHandle WindowInputTests InternalWindowOperationPerfTest Change-Id: Ief84bbe6e6fa4da5309912059904932ccf775b75 --- .../android/view/InputApplicationHandle.java | 12 +- core/java/android/view/InputWindowHandle.java | 2 +- ..._hardware_input_InputApplicationHandle.cpp | 22 +- .../com/android/server/wm/ActivityRecord.java | 18 +- .../java/com/android/server/wm/DragState.java | 5 +- .../server/wm/EmbeddedWindowController.java | 4 +- .../android/server/wm/InputConsumerImpl.java | 13 +- .../com/android/server/wm/InputMonitor.java | 209 +++++++------ .../server/wm/InputWindowHandleWrapper.java | 283 ++++++++++++++++++ .../com/android/server/wm/TaskPositioner.java | 6 +- .../server/wm/WindowManagerService.java | 2 +- .../com/android/server/wm/WindowState.java | 89 +++--- .../server/wm/WindowStateAnimator.java | 3 + .../android/server/wm/WindowStateTests.java | 55 +++- 14 files changed, 535 insertions(+), 188 deletions(-) create mode 100644 services/core/java/com/android/server/wm/InputWindowHandleWrapper.java diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java index 108345e6db0e8..4abffde68ffda 100644 --- a/core/java/android/view/InputApplicationHandle.java +++ b/core/java/android/view/InputApplicationHandle.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.NonNull; import android.os.IBinder; /** @@ -31,17 +32,20 @@ public final class InputApplicationHandle { private long ptr; // Application name. - public String name; + public final @NonNull String name; // Dispatching timeout. - public long dispatchingTimeoutMillis; + public final long dispatchingTimeoutMillis; - public final IBinder token; + public final @NonNull IBinder token; private native void nativeDispose(); - public InputApplicationHandle(IBinder token) { + public InputApplicationHandle(@NonNull IBinder token, @NonNull String name, + long dispatchingTimeoutMillis) { this.token = token; + this.name = name; + this.dispatchingTimeoutMillis = dispatchingTimeoutMillis; } public InputApplicationHandle(InputApplicationHandle handle) { diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index d1a9a05d5bf15..5a34a92a4b1a0 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -37,7 +37,7 @@ public final class InputWindowHandle { private long ptr; // The input application handle. - public final InputApplicationHandle inputApplicationHandle; + public InputApplicationHandle inputApplicationHandle; // The token associates input data with a window and its input channel. The client input // channel and the server input channel will both contain this token. diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp index 7756a62df655b..995bfa97ab2e8 100644 --- a/core/jni/android_hardware_input_InputApplicationHandle.cpp +++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp @@ -54,26 +54,28 @@ jobject NativeInputApplicationHandle::getInputApplicationHandleObjLocalRef(JNIEn bool NativeInputApplicationHandle::updateInfo() { JNIEnv* env = AndroidRuntime::getJNIEnv(); - jobject obj = env->NewLocalRef(mObjWeak); - if (!obj) { + ScopedLocalRef obj(env, env->NewLocalRef(mObjWeak)); + if (!obj.get()) { return false; } + if (mInfo.token.get() != nullptr) { + // The java fields are immutable, so it doesn't need to update again. + return true; + } - mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, ""); + mInfo.name = getStringField(env, obj.get(), gInputApplicationHandleClassInfo.name, ""); mInfo.dispatchingTimeoutMillis = - env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutMillis); + env->GetLongField(obj.get(), gInputApplicationHandleClassInfo.dispatchingTimeoutMillis); - jobject tokenObj = env->GetObjectField(obj, - gInputApplicationHandleClassInfo.token); - if (tokenObj) { - mInfo.token = ibinderForJavaObject(env, tokenObj); - env->DeleteLocalRef(tokenObj); + ScopedLocalRef tokenObj(env, env->GetObjectField(obj.get(), + gInputApplicationHandleClassInfo.token)); + if (tokenObj.get()) { + mInfo.token = ibinderForJavaObject(env, tokenObj.get()); } else { mInfo.token.clear(); } - env->DeleteLocalRef(obj); return mInfo.token.get() != nullptr; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0a7f08bfbe8cb..52b08ddcac6a7 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -417,7 +417,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // mOccludesParent field. final boolean hasWallpaper; // Input application handle used by the input dispatcher. - final InputApplicationHandle mInputApplicationHandle; + private InputApplicationHandle mInputApplicationHandle; final int launchedFromPid; // always the pid who started the activity. final int launchedFromUid; // always the uid who started the activity. @@ -1506,7 +1506,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A info = aInfo; mUserId = UserHandle.getUserId(info.applicationInfo.uid); packageName = info.applicationInfo.packageName; - mInputApplicationHandle = new InputApplicationHandle(appToken); intent = _intent; // If the class name in the intent doesn't match that of the target, this is probably an @@ -1693,6 +1692,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return lockTaskLaunchMode; } + @NonNull InputApplicationHandle getInputApplicationHandle(boolean update) { + if (mInputApplicationHandle == null) { + mInputApplicationHandle = new InputApplicationHandle(appToken, toString(), + mInputDispatchingTimeoutMillis); + } else if (update) { + final String name = toString(); + if (mInputDispatchingTimeoutMillis != mInputApplicationHandle.dispatchingTimeoutMillis + || !name.equals(mInputApplicationHandle.name)) { + mInputApplicationHandle = new InputApplicationHandle(appToken, name, + mInputDispatchingTimeoutMillis); + } + } + return mInputApplicationHandle; + } + @Override ActivityRecord asActivityRecord() { // I am an activity record! diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 2ea4b57d9de39..8c80205a9e457 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -277,9 +277,8 @@ class DragState { mInputEventReceiver = new DragInputEventReceiver(mClientChannel, mService.mH.getLooper(), mDragDropController); - mDragApplicationHandle = new InputApplicationHandle(new Binder()); - mDragApplicationHandle.name = "drag"; - mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + mDragApplicationHandle = new InputApplicationHandle(new Binder(), "drag", + DEFAULT_DISPATCHING_TIMEOUT_MILLIS); mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, display.getDisplayId()); diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 3b89a24184f0a..b08d6e1dff9e3 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -175,11 +175,11 @@ class EmbeddedWindowController { InputApplicationHandle getApplicationHandle() { if (mHostWindowState == null - || mHostWindowState.mInputWindowHandle.inputApplicationHandle == null) { + || mHostWindowState.mInputWindowHandle.getInputApplicationHandle() == null) { return null; } return new InputApplicationHandle( - mHostWindowState.mInputWindowHandle.inputApplicationHandle); + mHostWindowState.mInputWindowHandle.getInputApplicationHandle()); } InputChannel openInputChannel() { diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index edb5e853af4f6..e35621a84b37d 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -63,9 +63,8 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mClientChannel.copyTo(inputChannel); } - mApplicationHandle = new InputApplicationHandle(new Binder()); - mApplicationHandle.name = name; - mApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + mApplicationHandle = new InputApplicationHandle(new Binder(), name, + DEFAULT_DISPATCHING_TIMEOUT_MILLIS); mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId); mWindowHandle.name = name; @@ -160,9 +159,11 @@ class InputConsumerImpl implements IBinder.DeathRecipient { public void binderDied() { synchronized (mService.getWindowManagerLock()) { // Clean up the input consumer - final InputMonitor inputMonitor = - mService.mRoot.getDisplayContent(mWindowHandle.displayId).getInputMonitor(); - inputMonitor.destroyInputConsumer(mName); + final DisplayContent dc = mService.mRoot.getDisplayContent(mWindowHandle.displayId); + if (dc == null) { + return; + } + dc.getInputMonitor().destroyInputConsumer(mName); unlinkFromDeathRecipient(); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 4a54196b71de9..43c47417d95ff 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -48,6 +48,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS; import android.graphics.Rect; +import android.graphics.Region; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -57,12 +58,12 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.EventLog; import android.util.Slog; -import android.view.InputApplicationHandle; import android.view.InputChannel; import android.view.InputEventReceiver; import android.view.InputWindowHandle; import android.view.SurfaceControl; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import java.io.PrintWriter; @@ -81,7 +82,7 @@ final class InputMonitor { private boolean mUpdateInputWindowsImmediately; private boolean mDisableWallpaperTouchEvents; - private final Rect mTmpRect = new Rect(); + private final Region mTmpRegion = new Region(); private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer; private final int mDisplayId; @@ -276,66 +277,66 @@ final class InputMonitor { addInputConsumer(name, consumer); } - - void populateInputWindowHandle(final InputWindowHandle inputWindowHandle, - final WindowState child, int flags, final int type, final boolean isVisible, - final boolean focusable, final boolean hasWallpaper) { + @VisibleForTesting + void populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle, + final WindowState w) { // Add a window to our list of input windows. - inputWindowHandle.name = child.toString(); - flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags); - inputWindowHandle.layoutParamsFlags = flags; - inputWindowHandle.layoutParamsType = type; - inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis(); - inputWindowHandle.visible = isVisible; - inputWindowHandle.focusable = focusable; - inputWindowHandle.touchOcclusionMode = child.getTouchOcclusionMode(); - inputWindowHandle.hasWallpaper = hasWallpaper; - inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false; - inputWindowHandle.ownerPid = child.mSession.mPid; - inputWindowHandle.ownerUid = child.mSession.mUid; - inputWindowHandle.packageName = child.getOwningPackage(); - inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; - inputWindowHandle.displayId = child.getDisplayId(); + inputWindowHandle.setInputApplicationHandle(w.mActivityRecord != null + ? w.mActivityRecord.getInputApplicationHandle(false /* update */) : null); + inputWindowHandle.setToken(w.mInputChannelToken); + inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis()); + inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode()); + inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures); + inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused); + inputWindowHandle.setVisible(w.isVisible()); - final Rect frame = child.getFrame(); - inputWindowHandle.frameLeft = frame.left; - inputWindowHandle.frameTop = frame.top; - inputWindowHandle.frameRight = frame.right; - inputWindowHandle.frameBottom = frame.bottom; + final boolean focusable = w.canReceiveKeys() + && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop()); + inputWindowHandle.setFocusable(focusable); + + final boolean hasWallpaper = mDisplayContent.mWallpaperController.isWallpaperTarget(w) + && !mService.mPolicy.isKeyguardShowing() + && !mDisableWallpaperTouchEvents; + inputWindowHandle.setHasWallpaper(hasWallpaper); + + final Rect frame = w.getFrame(); + inputWindowHandle.setFrame(frame.left, frame.top, frame.right, frame.bottom); // Surface insets are hardcoded to be the same in all directions // and we could probably deprecate the "left/right/top/bottom" concept. // we avoid reintroducing this concept by just choosing one of them here. - inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left; + inputWindowHandle.setSurfaceInset(w.mAttrs.surfaceInsets.left); - /** - * If the window is in a TaskManaged by a TaskOrganizer then most cropping - * will be applied using the SurfaceControl hierarchy from the Organizer. - * This means we need to make sure that these changes in crop are reflected - * in the input windows, and so ensure this flag is set so that - * the input crop always reflects the surface hierarchy. - * - * TODO(b/168252846): we have some issues with modal-windows, so we need to - * cross that bridge now that we organize full-screen Tasks. - */ - if (child.getTask() != null - && child.getTask().isOrganized() - && child.getTask().getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { - inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */); + // If we are scaling the window, input coordinates need to be inversely scaled to map from + // what is on screen to what is actually being touched in the UI. + inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f); + + final int flags = w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs.flags); + inputWindowHandle.setTouchableRegion(mTmpRegion); + inputWindowHandle.setLayoutParamsFlags(flags); + + boolean useSurfaceCrop = false; + final Task task = w.getTask(); + if (task != null) { + if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + // If the window is in a TaskManaged by a TaskOrganizer then most cropping will + // be applied using the SurfaceControl hierarchy from the Organizer. This means + // we need to make sure that these changes in crop are reflected in the input + // windows, and so ensure this flag is set so that the input crop always reflects + // the surface hierarchy. + // TODO(b/168252846): we have some issues with modal-windows, so we need to cross + // that bridge now that we organize full-screen Tasks. + inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */); + useSurfaceCrop = true; + } else if (task.cropWindowsToStackBounds() && !w.inFreeformWindowingMode()) { + inputWindowHandle.replaceTouchableRegionWithCrop( + task.getRootTask().getSurfaceControl()); + useSurfaceCrop = true; + } } - - if (child.mGlobalScale != 1) { - // If we are scaling the window, input coordinates need - // to be inversely scaled to map from what is on screen - // to what is actually being touched in the UI. - inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; - } else { - inputWindowHandle.scaleFactor = 1; - } - - if (DEBUG_INPUT) { - Slog.d(TAG_WM, "addInputWindowHandle: " - + child + ", " + inputWindowHandle); + if (!useSurfaceCrop) { + inputWindowHandle.setReplaceTouchableRegionWithCrop(false); + inputWindowHandle.setTouchableRegionCrop(null); } } @@ -401,15 +402,8 @@ final class InputMonitor { public void setFocusedAppLw(ActivityRecord newApp) { // Focused app has changed. - if (newApp == null) { - mService.mInputManager.setFocusedApplication(mDisplayId, null); - } else { - final InputApplicationHandle handle = newApp.mInputApplicationHandle; - handle.name = newApp.toString(); - handle.dispatchingTimeoutMillis = newApp.mInputDispatchingTimeoutMillis; - - mService.mInputManager.setFocusedApplication(mDisplayId, handle); - } + mService.mInputManager.setFocusedApplication(mDisplayId, + newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null); } public void pauseDispatchingLw(WindowToken window) { @@ -456,10 +450,6 @@ final class InputMonitor { private boolean mAddRecentsAnimationInputConsumerHandle; boolean mInDrag; - WallpaperController mWallpaperController; - - // An invalid window handle that tells SurfaceFlinger not update the input info. - final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, mDisplayId); private void updateInputWindows(boolean inDrag) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); @@ -474,10 +464,8 @@ final class InputMonitor { mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null; mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null; - mTmpRect.setEmpty(); mDisableWallpaperTouchEvents = false; mInDrag = inDrag; - mWallpaperController = mDisplayContent.mWallpaperController; resetInputConsumers(mInputTransaction); @@ -499,7 +487,7 @@ final class InputMonitor { } final WindowState focus = mDisplayContent.mCurrentFocus; - if (focus == null || focus.mInputWindowHandle.token == null) { + if (focus == null || focus.mInputChannelToken == null) { mDisplayContent.mLastRequestedFocus = focus; return; } @@ -510,7 +498,7 @@ final class InputMonitor { return; } - mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId); + mInputTransaction.setFocusedWindow(focus.mInputChannelToken, mDisplayId); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus, "reason=UpdateInputWindows"); mDisplayContent.mLastRequestedFocus = focus; @@ -519,33 +507,26 @@ final class InputMonitor { @Override public void accept(WindowState w) { - final InputChannel inputChannel = w.mInputChannel; - final InputWindowHandle inputWindowHandle = w.mInputWindowHandle; + final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle; final RecentsAnimationController recentsAnimationController = mService.getRecentsAnimationController(); final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); - final int type = w.mAttrs.type; - final boolean isVisible = w.isVisibleLw(); - if (inputChannel == null || inputWindowHandle == null || w.mRemoved + if (w.mInputChannelToken == null || w.mRemoved || (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { if (w.mWinAnimator.hasSurface()) { // Assign an InputInfo with type to the overlay window which can't receive input // event. This is used to omit Surfaces from occlusion detection. - populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible); - mInputTransaction.setInputWindowInfo( - w.mWinAnimator.mSurfaceController.mSurfaceControl, - mInvalidInputWindow); + populateOverlayInputInfo(inputWindowHandle, w.isVisible()); + setInputWindowInfoIfNeeded(mInputTransaction, + w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); return; } // Skip this window because it cannot possibly receive input. return; } - final int flags = w.mAttrs.flags; final int privateFlags = w.mAttrs.privateFlags; - final boolean focusable = w.canReceiveKeys() - && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop()); if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( @@ -584,47 +565,53 @@ final class InputMonitor { if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { mDisableWallpaperTouchEvents = true; } - final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(w) - && !mService.mPolicy.isKeyguardShowing() - && !mDisableWallpaperTouchEvents; // If there's a drag in progress and 'child' is a potential drop target, // make sure it's been told about the drag - if (mInDrag && isVisible && w.getDisplayContent().isDefaultDisplay) { + if (mInDrag && w.isVisible() && w.getDisplayContent().isDefaultDisplay) { mService.mDragDropController.sendDragStartedIfNeededLocked(w); } - populateInputWindowHandle( - inputWindowHandle, w, flags, type, isVisible, focusable, hasWallpaper); - // register key interception info - mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token, + mService.mKeyInterceptionInfoForToken.put(w.mInputChannelToken, w.getKeyInterceptionInfo()); if (w.mWinAnimator.hasSurface()) { - mInputTransaction.setInputWindowInfo( + populateInputWindowHandle(inputWindowHandle, w); + setInputWindowInfoIfNeeded(mInputTransaction, w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); } } } + @VisibleForTesting + static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc, + InputWindowHandleWrapper inputWindowHandle) { + if (DEBUG_INPUT) { + Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle); + } + if (inputWindowHandle.isChanged()) { + inputWindowHandle.applyChangesToSurface(t, sc); + } + } + // This would reset InputWindowHandle fields to prevent it could be found by input event. // We need to check if any new field of InputWindowHandle could impact the result. - private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle, - final String name, final int type, final boolean isVisible) { - inputWindowHandle.name = name; - inputWindowHandle.layoutParamsType = type; - inputWindowHandle.dispatchingTimeoutMillis = 0; // it should never receive input - inputWindowHandle.visible = isVisible; - inputWindowHandle.focusable = false; - inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL; - inputWindowHandle.scaleFactor = 1; - inputWindowHandle.layoutParamsFlags = - FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE; - inputWindowHandle.portalToDisplayId = INVALID_DISPLAY; - inputWindowHandle.touchableRegion.setEmpty(); + @VisibleForTesting + static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle, + boolean isVisible) { + inputWindowHandle.setDispatchingTimeoutMillis(0); // It should never receive input. + inputWindowHandle.setVisible(isVisible); + inputWindowHandle.setFocusable(false); + inputWindowHandle.setInputFeatures(INPUT_FEATURE_NO_INPUT_CHANNEL); + // The input window handle without input channel must not have a token. + inputWindowHandle.setToken(null); + inputWindowHandle.setScaleFactor(1f); + inputWindowHandle.setLayoutParamsFlags( + FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE); + inputWindowHandle.setPortalToDisplayId(INVALID_DISPLAY); + inputWindowHandle.clearTouchableRegion(); inputWindowHandle.setTouchableRegionCrop(null); - inputWindowHandle.trustedOverlay = isTrustedOverlay(type); } /** @@ -635,9 +622,13 @@ final class InputMonitor { */ static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t, int displayId, String name) { - InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId); - populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true); - t.setInputWindowInfo(sc, inputWindowHandle); + final InputWindowHandleWrapper inputWindowHandle = new InputWindowHandleWrapper( + new InputWindowHandle(null /* inputApplicationHandle */, displayId)); + inputWindowHandle.setName(name); + inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY); + inputWindowHandle.setTrustedOverlay(true); + populateOverlayInputInfo(inputWindowHandle, true /* isVisible */); + setInputWindowInfoIfNeeded(t, sc, inputWindowHandle); } static boolean isTrustedOverlay(int type) { diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java new file mode 100644 index 0000000000000..1fbeb1fd456b6 --- /dev/null +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Region; +import android.os.IBinder; +import android.view.InputApplicationHandle; +import android.view.InputWindowHandle; +import android.view.SurfaceControl; + +import java.util.Objects; + +/** + * The wrapper of {@link InputWindowHandle} with field change detection to reduce unnecessary + * updates to surface, e.g. if there are no changes, then skip invocation of + * {@link SurfaceControl.Transaction#setInputWindowInfo(SurfaceControl, InputWindowHandle)}. + */ +class InputWindowHandleWrapper { + /** The wrapped handle should not be directly exposed to avoid untracked changes. */ + private final @NonNull InputWindowHandle mHandle; + + /** Whether the {@link #mHandle} is changed. */ + private boolean mChanged = true; + + InputWindowHandleWrapper(@NonNull InputWindowHandle handle) { + mHandle = handle; + } + + /** + * Returns {@code true} if the input window handle has changed since the last invocation of + * {@link #applyChangesToSurface(SurfaceControl.Transaction, SurfaceControl)}} + */ + boolean isChanged() { + return mChanged; + } + + void forceChange() { + mChanged = true; + } + + void applyChangesToSurface(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) { + t.setInputWindowInfo(sc, mHandle); + mChanged = false; + } + + int getDisplayId() { + return mHandle.displayId; + } + + InputApplicationHandle getInputApplicationHandle() { + return mHandle.inputApplicationHandle; + } + + void setInputApplicationHandle(InputApplicationHandle handle) { + if (mHandle.inputApplicationHandle == handle) { + return; + } + mHandle.inputApplicationHandle = handle; + mChanged = true; + } + + void setToken(IBinder token) { + if (mHandle.token == token) { + return; + } + mHandle.token = token; + mChanged = true; + } + + void setName(String name) { + if (Objects.equals(mHandle.name, name)) { + return; + } + mHandle.name = name; + mChanged = true; + } + + void setLayoutParamsFlags(int flags) { + if (mHandle.layoutParamsFlags == flags) { + return; + } + mHandle.layoutParamsFlags = flags; + mChanged = true; + } + + void setLayoutParamsType(int type) { + if (mHandle.layoutParamsType == type) { + return; + } + mHandle.layoutParamsType = type; + mChanged = true; + } + + void setDispatchingTimeoutMillis(long timeout) { + if (mHandle.dispatchingTimeoutMillis == timeout) { + return; + } + mHandle.dispatchingTimeoutMillis = timeout; + mChanged = true; + } + + void setTouchableRegion(Region region) { + if (mHandle.touchableRegion.equals(region)) { + return; + } + mHandle.touchableRegion.set(region); + mChanged = true; + } + + void clearTouchableRegion() { + if (mHandle.touchableRegion.isEmpty()) { + return; + } + mHandle.touchableRegion.setEmpty(); + mChanged = true; + } + + void setVisible(boolean visible) { + if (mHandle.visible == visible) { + return; + } + mHandle.visible = visible; + mChanged = true; + } + + void setFocusable(boolean focusable) { + if (mHandle.focusable == focusable) { + return; + } + mHandle.focusable = focusable; + mChanged = true; + } + + void setTouchOcclusionMode(int mode) { + if (mHandle.touchOcclusionMode == mode) { + return; + } + mHandle.touchOcclusionMode = mode; + mChanged = true; + } + + void setHasWallpaper(boolean hasWallpaper) { + if (mHandle.hasWallpaper == hasWallpaper) { + return; + } + mHandle.hasWallpaper = hasWallpaper; + mChanged = true; + } + + void setPaused(boolean paused) { + if (mHandle.paused == paused) { + return; + } + mHandle.paused = paused; + mChanged = true; + } + + void setTrustedOverlay(boolean trustedOverlay) { + if (mHandle.trustedOverlay == trustedOverlay) { + return; + } + mHandle.trustedOverlay = trustedOverlay; + mChanged = true; + } + + void setOwnerPid(int pid) { + if (mHandle.ownerPid == pid) { + return; + } + mHandle.ownerPid = pid; + mChanged = true; + } + + void setOwnerUid(int uid) { + if (mHandle.ownerUid == uid) { + return; + } + mHandle.ownerUid = uid; + mChanged = true; + } + + void setPackageName(String packageName) { + if (Objects.equals(mHandle.packageName, packageName)) { + return; + } + mHandle.packageName = packageName; + mChanged = true; + } + + void setInputFeatures(int features) { + if (mHandle.inputFeatures == features) { + return; + } + mHandle.inputFeatures = features; + mChanged = true; + } + + void setDisplayId(int displayId) { + if (mHandle.displayId == displayId) { + return; + } + mHandle.displayId = displayId; + mChanged = true; + } + + void setPortalToDisplayId(int displayId) { + if (mHandle.portalToDisplayId == displayId) { + return; + } + mHandle.portalToDisplayId = displayId; + mChanged = true; + } + + void setFrame(int left, int top, int right, int bottom) { + if (mHandle.frameLeft == left && mHandle.frameTop == top && mHandle.frameRight == right + && mHandle.frameBottom == bottom) { + return; + } + mHandle.frameLeft = left; + mHandle.frameTop = top; + mHandle.frameRight = right; + mHandle.frameBottom = bottom; + mChanged = true; + } + + void setSurfaceInset(int inset) { + if (mHandle.surfaceInset == inset) { + return; + } + mHandle.surfaceInset = inset; + mChanged = true; + } + + void setScaleFactor(float scale) { + if (mHandle.scaleFactor == scale) { + return; + } + mHandle.scaleFactor = scale; + mChanged = true; + } + + void replaceTouchableRegionWithCrop(@Nullable SurfaceControl bounds) { + setTouchableRegionCrop(bounds); + setReplaceTouchableRegionWithCrop(true); + } + + void setTouchableRegionCrop(@Nullable SurfaceControl bounds) { + if (mHandle.touchableRegionSurfaceControl.get() == bounds) { + return; + } + mHandle.setTouchableRegionCrop(bounds); + mChanged = true; + } + + void setReplaceTouchableRegionWithCrop(boolean replace) { + if (mHandle.replaceTouchableRegionWithCrop == replace) { + return; + } + mHandle.replaceTouchableRegionWithCrop = replace; + mChanged = true; + } + + @Override + public String toString() { + return mHandle + ", changed=" + mChanged; + } +} diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index a6f0f464c8b38..614d2210189c2 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -225,10 +225,8 @@ class TaskPositioner implements IBinder.DeathRecipient { mClientChannel, mService.mAnimationHandler.getLooper(), mService.mAnimator.getChoreographer()); - mDragApplicationHandle = new InputApplicationHandle(new Binder()); - mDragApplicationHandle.name = TAG; - mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; - + mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG, + DEFAULT_DISPATCHING_TIMEOUT_MILLIS); mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, displayContent.getDisplayId()); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 084b7667b5a55..5ada647e56630 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8365,7 +8365,7 @@ public class WindowManagerService extends IWindowManager.Stub embeddedWindow.getName()); return; } - t.requestFocusTransfer(newFocusTarget.mInputWindowHandle.token, targetInputToken, + t.requestFocusTransfer(newFocusTarget.mInputChannelToken, targetInputToken, displayId).apply(); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Transfer focus request " + newFocusTarget, diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ab2f424261533..b5f4560531c16 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -555,9 +555,17 @@ class WindowState extends WindowContainer implements WindowManagerP boolean mWindowRemovalAllowed; // Input channel and input window handle used by the input dispatcher. - final InputWindowHandle mInputWindowHandle; + final InputWindowHandleWrapper mInputWindowHandle; InputChannel mInputChannel; + /** + * The token will be assigned to {@link InputWindowHandle#token} if this window can receive + * input event. Note that the token of associated input window handle can be cleared if this + * window becomes unable to receive input, but this field will remain until the input channel + * is actually disposed. + */ + IBinder mInputChannelToken; + // Used to improve performance of toString() private String mStringNameCache; private CharSequence mLastTitle; @@ -855,6 +863,21 @@ class WindowState extends WindowContainer implements WindowManagerP DeathRecipient deathRecipient = new DeathRecipient(); mPowerManagerWrapper = powerManagerWrapper; mForceSeamlesslyRotate = token.mRoundedCornerOverlay; + mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle( + mActivityRecord != null + ? mActivityRecord.getInputApplicationHandle(false /* update */) : null, + getDisplayId())); + mInputWindowHandle.setOwnerPid(s.mPid); + mInputWindowHandle.setOwnerUid(s.mUid); + mInputWindowHandle.setName(getName()); + mInputWindowHandle.setPackageName(mAttrs.packageName); + mInputWindowHandle.setLayoutParamsType(mAttrs.type); + // Check private trusted overlay flag and window type to set trustedOverlay variable of + // input window handle. + mInputWindowHandle.setTrustedOverlay( + ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0 + && mOwnerCanAddInternalSystemWindow) + || InputMonitor.isTrustedOverlay(mAttrs.type)); if (DEBUG) { Slog.v(TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); @@ -870,7 +893,6 @@ class WindowState extends WindowContainer implements WindowManagerP mIsFloatingLayer = false; mBaseLayer = 0; mSubLayer = 0; - mInputWindowHandle = null; mWinAnimator = null; mWpcForDisplayConfigChanges = null; return; @@ -918,16 +940,6 @@ class WindowState extends WindowContainer implements WindowManagerP mLastRequestedWidth = 0; mLastRequestedHeight = 0; mLayer = 0; - mInputWindowHandle = new InputWindowHandle( - mActivityRecord != null ? mActivityRecord.mInputApplicationHandle : null, - getDisplayId()); - - // Check private trusted overlay flag and window type to set trustedOverlay variable of - // input window handle. - mInputWindowHandle.trustedOverlay = - (mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0 - && mOwnerCanAddInternalSystemWindow; - mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type); // Make sure we initial all fields before adding to parentWindow, to prevent exception // during onDisplayChanged. @@ -1495,9 +1507,9 @@ class WindowState extends WindowContainer implements WindowManagerP } super.onDisplayChanged(dc); // Window was not laid out for this display yet, so make sure mLayoutSeq does not match. - if (dc != null && mInputWindowHandle.displayId != dc.getDisplayId()) { + if (dc != null && mInputWindowHandle.getDisplayId() != dc.getDisplayId()) { mLayoutSeq = dc.mLayoutSeq - 1; - mInputWindowHandle.displayId = dc.getDisplayId(); + mInputWindowHandle.setDisplayId(dc.getDisplayId()); } } @@ -2469,7 +2481,9 @@ class WindowState extends WindowContainer implements WindowManagerP } String name = getName(); mInputChannel = mWmService.mInputManager.createInputChannel(name); - mInputWindowHandle.token = mInputChannel.getToken(); + mInputChannelToken = mInputChannel.getToken(); + mInputWindowHandle.setToken(mInputChannelToken); + mWmService.mInputToWindowMap.put(mInputChannelToken, this); if (outInputChannel != null) { mInputChannel.copyTo(outInputChannel); } else { @@ -2478,7 +2492,6 @@ class WindowState extends WindowContainer implements WindowManagerP // Create fake event receiver that simply reports all events as handled. mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel); } - mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this); } void disposeInputChannel() { @@ -2486,17 +2499,19 @@ class WindowState extends WindowContainer implements WindowManagerP mDeadWindowEventReceiver.dispose(); mDeadWindowEventReceiver = null; } + if (mInputChannelToken != null) { + // Unregister server channel first otherwise it complains about broken channel. + mWmService.mInputManager.removeInputChannel(mInputChannelToken); + mWmService.mKeyInterceptionInfoForToken.remove(mInputChannelToken); + mWmService.mInputToWindowMap.remove(mInputChannelToken); + mInputChannelToken = null; + } - // unregister server channel first otherwise it complains about broken channel if (mInputChannel != null) { - mWmService.mInputManager.removeInputChannel(mInputChannel.getToken()); - mInputChannel.dispose(); mInputChannel = null; } - mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token); - mWmService.mInputToWindowMap.remove(mInputWindowHandle.token); - mInputWindowHandle.token = null; + mInputWindowHandle.setToken(null); } /** Returns true if the replacement window was removed. */ @@ -2566,11 +2581,8 @@ class WindowState extends WindowContainer implements WindowManagerP } } - int getSurfaceTouchableRegion(InputWindowHandle inputWindowHandle, int flags) { + int getSurfaceTouchableRegion(Region region, int flags) { final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0; - final Region region = inputWindowHandle.touchableRegion; - setTouchableRegionCropIfNeeded(inputWindowHandle); - if (modal) { flags |= FLAG_NOT_TOUCH_MODAL; if (mActivityRecord != null) { @@ -2592,7 +2604,10 @@ class WindowState extends WindowContainer implements WindowManagerP } // Translate to surface based coordinates. - region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top); + final Rect frame = mWindowFrames.mFrame; + if (frame.left != 0 || frame.top != 0) { + region.translate(-frame.left, -frame.top); + } // TODO(b/139804591): sizecompat layout needs to be reworked. Currently mFrame is post- // scaling but the existing logic doesn't expect that. The result is that the already- @@ -3441,7 +3456,9 @@ class WindowState extends WindowContainer implements WindowManagerP break; case TOUCHABLE_INSETS_REGION: { outRegion.set(mGivenTouchableRegion); - outRegion.translate(frame.left, frame.top); + if (frame.left != 0 || frame.top != 0) { + outRegion.translate(frame.left, frame.top); + } break; } } @@ -3468,22 +3485,6 @@ class WindowState extends WindowContainer implements WindowManagerP } } - private void setTouchableRegionCropIfNeeded(InputWindowHandle handle) { - final Task task = getTask(); - if (task == null || !task.cropWindowsToStackBounds()) { - handle.setTouchableRegionCrop(null); - return; - } - - final Task stack = task.getRootTask(); - if (stack == null || inFreeformWindowingMode()) { - handle.setTouchableRegionCrop(null); - return; - } - - handle.setTouchableRegionCrop(stack.getSurfaceControl()); - } - private void cropRegionToStackBoundsIfNeeded(Region region) { final Task task = getTask(); if (task == null || !task.cropWindowsToStackBounds()) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 1e959e54555f9..7b1b0d394e5ce 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -488,6 +488,9 @@ class WindowStateAnimator { mSurfaceFormat = format; w.setHasSurface(true); + // The surface instance is changed. Make sure the input info can be applied to the + // new surface, e.g. relaunch activity. + w.mInputWindowHandle.forceChange(); ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s", 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 2691ae84db287..7787b919a2bf6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -63,15 +63,18 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.when; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.Size; import android.view.DisplayCutout; +import android.view.InputWindowHandle; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.WindowManager; @@ -466,10 +469,10 @@ public class WindowStateTests extends WindowTestsBase { public void testDisplayIdUpdatedOnReparent() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); // fake a different display - app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1; + app.mInputWindowHandle.setDisplayId(mDisplayContent.getDisplayId() + 1); app.onDisplayChanged(mDisplayContent); - assertThat(app.mInputWindowHandle.displayId, is(mDisplayContent.getDisplayId())); + assertThat(app.mInputWindowHandle.getDisplayId(), is(mDisplayContent.getDisplayId())); assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId())); } @@ -680,6 +683,54 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(win0.canReceiveTouchInput()); } + @Test + public void testUpdateInputWindowHandle() { + final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); + win.mAttrs.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; + final InputWindowHandle handle = new InputWindowHandle( + win.mInputWindowHandle.getInputApplicationHandle(), win.getDisplayId()); + final InputWindowHandleWrapper handleWrapper = new InputWindowHandleWrapper(handle); + final IBinder inputChannelToken = mock(IBinder.class); + win.mInputChannelToken = inputChannelToken; + + mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win); + + assertTrue(handleWrapper.isChanged()); + assertEquals(inputChannelToken, handle.token); + assertEquals(win.mActivityRecord.getInputApplicationHandle(false /* update */), + handle.inputApplicationHandle); + assertEquals(win.mAttrs.inputFeatures, handle.inputFeatures); + assertEquals(win.isVisible(), handle.visible); + + final SurfaceControl sc = mock(SurfaceControl.class); + final SurfaceControl.Transaction transaction = mSystemServicesTestRule.mTransaction; + InputMonitor.setInputWindowInfoIfNeeded(transaction, sc, handleWrapper); + + // The fields of input window handle are changed, so it must set input window info + // successfully. And then the changed flag should be reset. + verify(transaction).setInputWindowInfo(eq(sc), eq(handle)); + assertFalse(handleWrapper.isChanged()); + // Populate the same states again, the handle should not detect change. + mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win); + assertFalse(handleWrapper.isChanged()); + + // Apply the no change handle, the invocation of setInputWindowInfo should be skipped. + clearInvocations(transaction); + InputMonitor.setInputWindowInfoIfNeeded(transaction, sc, handleWrapper); + verify(transaction, never()).setInputWindowInfo(any(), any()); + + // Populate as an overlay to disable the input of window. + InputMonitor.populateOverlayInputInfo(handleWrapper, false /* isVisible */); + // The overlay attributes should be set. + assertTrue(handleWrapper.isChanged()); + assertFalse(handle.focusable); + assertFalse(handle.visible); + assertNull(handle.token); + assertEquals(0L, handle.dispatchingTimeoutMillis); + assertEquals(WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL, + handle.inputFeatures); + } + @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testNeedsRelativeLayeringToIme_notAttached() {