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 a35183e666ca7..19b13c1bcc6f0 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 b8bf94c33ef15..e7d9e6b63bb69 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8395,7 +8395,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 9234390940f76..fc064615d00e8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -556,9 +556,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; @@ -856,6 +864,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); @@ -871,7 +894,6 @@ class WindowState extends WindowContainer implements WindowManagerP mIsFloatingLayer = false; mBaseLayer = 0; mSubLayer = 0; - mInputWindowHandle = null; mWinAnimator = null; mWpcForDisplayConfigChanges = null; return; @@ -919,16 +941,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. @@ -1496,9 +1508,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()); } } @@ -2470,7 +2482,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 { @@ -2479,7 +2493,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() { @@ -2487,17 +2500,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. */ @@ -2567,11 +2582,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) { @@ -2593,7 +2605,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- @@ -3442,7 +3457,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; } } @@ -3469,22 +3486,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 d845b123a5607..72aa7661a5286 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 9304dc53c819f..5c4563e0ce1b4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -62,15 +62,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; @@ -464,10 +467,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())); } @@ -678,6 +681,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() {