Merge "Reduce unnecessary invocation of setInputWindowInfo"

This commit is contained in:
Riddle Hsu
2020-11-04 05:35:51 +00:00
committed by Android (Google) Code Review
14 changed files with 535 additions and 188 deletions

View File

@@ -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) {

View File

@@ -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.

View File

@@ -54,26 +54,28 @@ jobject NativeInputApplicationHandle::getInputApplicationHandleObjLocalRef(JNIEn
bool NativeInputApplicationHandle::updateInfo() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject obj = env->NewLocalRef(mObjWeak);
if (!obj) {
ScopedLocalRef<jobject> 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, "<null>");
mInfo.name = getStringField(env, obj.get(), gInputApplicationHandleClassInfo.name, "<null>");
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<jobject> 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;
}

View File

@@ -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!

View File

@@ -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());

View File

@@ -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() {

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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());

View File

@@ -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,

View File

@@ -556,9 +556,17 @@ class WindowState extends WindowContainer<WindowState> 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<WindowState> 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<WindowState> implements WindowManagerP
mIsFloatingLayer = false;
mBaseLayer = 0;
mSubLayer = 0;
mInputWindowHandle = null;
mWinAnimator = null;
mWpcForDisplayConfigChanges = null;
return;
@@ -919,16 +941,6 @@ class WindowState extends WindowContainer<WindowState> 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<WindowState> 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<WindowState> 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<WindowState> 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<WindowState> 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<WindowState> 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<WindowState> 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<WindowState> 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<WindowState> 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()) {

View File

@@ -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",

View File

@@ -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() {