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
This commit is contained in:
Riddle Hsu
2020-10-30 15:07:28 +08:00
parent f2d1d26479
commit 3213c9657b
14 changed files with 535 additions and 188 deletions

View File

@@ -16,6 +16,7 @@
package android.view; package android.view;
import android.annotation.NonNull;
import android.os.IBinder; import android.os.IBinder;
/** /**
@@ -31,17 +32,20 @@ public final class InputApplicationHandle {
private long ptr; private long ptr;
// Application name. // Application name.
public String name; public final @NonNull String name;
// Dispatching timeout. // Dispatching timeout.
public long dispatchingTimeoutMillis; public final long dispatchingTimeoutMillis;
public final IBinder token; public final @NonNull IBinder token;
private native void nativeDispose(); private native void nativeDispose();
public InputApplicationHandle(IBinder token) { public InputApplicationHandle(@NonNull IBinder token, @NonNull String name,
long dispatchingTimeoutMillis) {
this.token = token; this.token = token;
this.name = name;
this.dispatchingTimeoutMillis = dispatchingTimeoutMillis;
} }
public InputApplicationHandle(InputApplicationHandle handle) { public InputApplicationHandle(InputApplicationHandle handle) {

View File

@@ -37,7 +37,7 @@ public final class InputWindowHandle {
private long ptr; private long ptr;
// The input application handle. // 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 // 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. // channel and the server input channel will both contain this token.

View File

@@ -54,26 +54,28 @@ jobject NativeInputApplicationHandle::getInputApplicationHandleObjLocalRef(JNIEn
bool NativeInputApplicationHandle::updateInfo() { bool NativeInputApplicationHandle::updateInfo() {
JNIEnv* env = AndroidRuntime::getJNIEnv(); JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject obj = env->NewLocalRef(mObjWeak); ScopedLocalRef<jobject> obj(env, env->NewLocalRef(mObjWeak));
if (!obj) { if (!obj.get()) {
return false; 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 = mInfo.dispatchingTimeoutMillis =
env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutMillis); env->GetLongField(obj.get(), gInputApplicationHandleClassInfo.dispatchingTimeoutMillis);
jobject tokenObj = env->GetObjectField(obj, ScopedLocalRef<jobject> tokenObj(env, env->GetObjectField(obj.get(),
gInputApplicationHandleClassInfo.token); gInputApplicationHandleClassInfo.token));
if (tokenObj) { if (tokenObj.get()) {
mInfo.token = ibinderForJavaObject(env, tokenObj); mInfo.token = ibinderForJavaObject(env, tokenObj.get());
env->DeleteLocalRef(tokenObj);
} else { } else {
mInfo.token.clear(); mInfo.token.clear();
} }
env->DeleteLocalRef(obj);
return mInfo.token.get() != nullptr; return mInfo.token.get() != nullptr;
} }

View File

@@ -417,7 +417,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// mOccludesParent field. // mOccludesParent field.
final boolean hasWallpaper; final boolean hasWallpaper;
// Input application handle used by the input dispatcher. // 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 launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid 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; info = aInfo;
mUserId = UserHandle.getUserId(info.applicationInfo.uid); mUserId = UserHandle.getUserId(info.applicationInfo.uid);
packageName = info.applicationInfo.packageName; packageName = info.applicationInfo.packageName;
mInputApplicationHandle = new InputApplicationHandle(appToken);
intent = _intent; intent = _intent;
// If the class name in the intent doesn't match that of the target, this is probably an // 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; 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 @Override
ActivityRecord asActivityRecord() { ActivityRecord asActivityRecord() {
// I am an activity record! // I am an activity record!

View File

@@ -277,9 +277,8 @@ class DragState {
mInputEventReceiver = new DragInputEventReceiver(mClientChannel, mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController); mService.mH.getLooper(), mDragDropController);
mDragApplicationHandle = new InputApplicationHandle(new Binder()); mDragApplicationHandle = new InputApplicationHandle(new Binder(), "drag",
mDragApplicationHandle.name = "drag"; DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
display.getDisplayId()); display.getDisplayId());

View File

@@ -175,11 +175,11 @@ class EmbeddedWindowController {
InputApplicationHandle getApplicationHandle() { InputApplicationHandle getApplicationHandle() {
if (mHostWindowState == null if (mHostWindowState == null
|| mHostWindowState.mInputWindowHandle.inputApplicationHandle == null) { || mHostWindowState.mInputWindowHandle.getInputApplicationHandle() == null) {
return null; return null;
} }
return new InputApplicationHandle( return new InputApplicationHandle(
mHostWindowState.mInputWindowHandle.inputApplicationHandle); mHostWindowState.mInputWindowHandle.getInputApplicationHandle());
} }
InputChannel openInputChannel() { InputChannel openInputChannel() {

View File

@@ -63,9 +63,8 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
mClientChannel.copyTo(inputChannel); mClientChannel.copyTo(inputChannel);
} }
mApplicationHandle = new InputApplicationHandle(new Binder()); mApplicationHandle = new InputApplicationHandle(new Binder(), name,
mApplicationHandle.name = name; DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
mApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId); mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name; mWindowHandle.name = name;
@@ -160,9 +159,11 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
public void binderDied() { public void binderDied() {
synchronized (mService.getWindowManagerLock()) { synchronized (mService.getWindowManagerLock()) {
// Clean up the input consumer // Clean up the input consumer
final InputMonitor inputMonitor = final DisplayContent dc = mService.mRoot.getDisplayContent(mWindowHandle.displayId);
mService.mRoot.getDisplayContent(mWindowHandle.displayId).getInputMonitor(); if (dc == null) {
inputMonitor.destroyInputConsumer(mName); return;
}
dc.getInputMonitor().destroyInputConsumer(mName);
unlinkFromDeathRecipient(); 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 static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
@@ -57,12 +58,12 @@ import android.os.UserHandle;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.EventLog; import android.util.EventLog;
import android.util.Slog; import android.util.Slog;
import android.view.InputApplicationHandle;
import android.view.InputChannel; import android.view.InputChannel;
import android.view.InputEventReceiver; import android.view.InputEventReceiver;
import android.view.InputWindowHandle; import android.view.InputWindowHandle;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog; import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -81,7 +82,7 @@ final class InputMonitor {
private boolean mUpdateInputWindowsImmediately; private boolean mUpdateInputWindowsImmediately;
private boolean mDisableWallpaperTouchEvents; private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect(); private final Region mTmpRegion = new Region();
private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer; private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
private final int mDisplayId; private final int mDisplayId;
@@ -276,66 +277,66 @@ final class InputMonitor {
addInputConsumer(name, consumer); addInputConsumer(name, consumer);
} }
@VisibleForTesting
void populateInputWindowHandle(final InputWindowHandle inputWindowHandle, void populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible, final WindowState w) {
final boolean focusable, final boolean hasWallpaper) {
// Add a window to our list of input windows. // Add a window to our list of input windows.
inputWindowHandle.name = child.toString(); inputWindowHandle.setInputApplicationHandle(w.mActivityRecord != null
flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags); ? w.mActivityRecord.getInputApplicationHandle(false /* update */) : null);
inputWindowHandle.layoutParamsFlags = flags; inputWindowHandle.setToken(w.mInputChannelToken);
inputWindowHandle.layoutParamsType = type; inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis());
inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis(); inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode());
inputWindowHandle.visible = isVisible; inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures);
inputWindowHandle.focusable = focusable; inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
inputWindowHandle.touchOcclusionMode = child.getTouchOcclusionMode(); inputWindowHandle.setVisible(w.isVisible());
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();
final Rect frame = child.getFrame(); final boolean focusable = w.canReceiveKeys()
inputWindowHandle.frameLeft = frame.left; && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
inputWindowHandle.frameTop = frame.top; inputWindowHandle.setFocusable(focusable);
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom; 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 // Surface insets are hardcoded to be the same in all directions
// and we could probably deprecate the "left/right/top/bottom" concept. // and we could probably deprecate the "left/right/top/bottom" concept.
// we avoid reintroducing this concept by just choosing one of them here. // 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 we are scaling the window, input coordinates need to be inversely scaled to map from
* If the window is in a TaskManaged by a TaskOrganizer then most cropping // what is on screen to what is actually being touched in the UI.
* will be applied using the SurfaceControl hierarchy from the Organizer. inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
* 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 final int flags = w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs.flags);
* the input crop always reflects the surface hierarchy. inputWindowHandle.setTouchableRegion(mTmpRegion);
* inputWindowHandle.setLayoutParamsFlags(flags);
* TODO(b/168252846): we have some issues with modal-windows, so we need to
* cross that bridge now that we organize full-screen Tasks. boolean useSurfaceCrop = false;
*/ final Task task = w.getTask();
if (child.getTask() != null if (task != null) {
&& child.getTask().isOrganized() if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
&& child.getTask().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 */); 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 (!useSurfaceCrop) {
if (DEBUG_INPUT) { inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
Slog.d(TAG_WM, "addInputWindowHandle: " inputWindowHandle.setTouchableRegionCrop(null);
+ child + ", " + inputWindowHandle);
} }
} }
@@ -401,15 +402,8 @@ final class InputMonitor {
public void setFocusedAppLw(ActivityRecord newApp) { public void setFocusedAppLw(ActivityRecord newApp) {
// Focused app has changed. // Focused app has changed.
if (newApp == null) { mService.mInputManager.setFocusedApplication(mDisplayId,
mService.mInputManager.setFocusedApplication(mDisplayId, null); newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null);
} else {
final InputApplicationHandle handle = newApp.mInputApplicationHandle;
handle.name = newApp.toString();
handle.dispatchingTimeoutMillis = newApp.mInputDispatchingTimeoutMillis;
mService.mInputManager.setFocusedApplication(mDisplayId, handle);
}
} }
public void pauseDispatchingLw(WindowToken window) { public void pauseDispatchingLw(WindowToken window) {
@@ -456,10 +450,6 @@ final class InputMonitor {
private boolean mAddRecentsAnimationInputConsumerHandle; private boolean mAddRecentsAnimationInputConsumerHandle;
boolean mInDrag; 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) { private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
@@ -474,10 +464,8 @@ final class InputMonitor {
mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null; mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null;
mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null; mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null;
mTmpRect.setEmpty();
mDisableWallpaperTouchEvents = false; mDisableWallpaperTouchEvents = false;
mInDrag = inDrag; mInDrag = inDrag;
mWallpaperController = mDisplayContent.mWallpaperController;
resetInputConsumers(mInputTransaction); resetInputConsumers(mInputTransaction);
@@ -499,7 +487,7 @@ final class InputMonitor {
} }
final WindowState focus = mDisplayContent.mCurrentFocus; final WindowState focus = mDisplayContent.mCurrentFocus;
if (focus == null || focus.mInputWindowHandle.token == null) { if (focus == null || focus.mInputChannelToken == null) {
mDisplayContent.mLastRequestedFocus = focus; mDisplayContent.mLastRequestedFocus = focus;
return; return;
} }
@@ -510,7 +498,7 @@ final class InputMonitor {
return; return;
} }
mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId); mInputTransaction.setFocusedWindow(focus.mInputChannelToken, mDisplayId);
EventLog.writeEvent(LOGTAG_INPUT_FOCUS, EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Focus request " + focus, "reason=UpdateInputWindows"); "Focus request " + focus, "reason=UpdateInputWindows");
mDisplayContent.mLastRequestedFocus = focus; mDisplayContent.mLastRequestedFocus = focus;
@@ -519,33 +507,26 @@ final class InputMonitor {
@Override @Override
public void accept(WindowState w) { public void accept(WindowState w) {
final InputChannel inputChannel = w.mInputChannel; final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle;
final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
final RecentsAnimationController recentsAnimationController = final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController(); mService.getRecentsAnimationController();
final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
&& recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
final int type = w.mAttrs.type; if (w.mInputChannelToken == null || w.mRemoved
final boolean isVisible = w.isVisibleLw();
if (inputChannel == null || inputWindowHandle == null || w.mRemoved
|| (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { || (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
if (w.mWinAnimator.hasSurface()) { if (w.mWinAnimator.hasSurface()) {
// Assign an InputInfo with type to the overlay window which can't receive input // Assign an InputInfo with type to the overlay window which can't receive input
// event. This is used to omit Surfaces from occlusion detection. // event. This is used to omit Surfaces from occlusion detection.
populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible); populateOverlayInputInfo(inputWindowHandle, w.isVisible());
mInputTransaction.setInputWindowInfo( setInputWindowInfoIfNeeded(mInputTransaction,
w.mWinAnimator.mSurfaceController.mSurfaceControl, w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
mInvalidInputWindow);
return; return;
} }
// Skip this window because it cannot possibly receive input. // Skip this window because it cannot possibly receive input.
return; return;
} }
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags; final int privateFlags = w.mAttrs.privateFlags;
final boolean focusable = w.canReceiveKeys()
&& (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp( if (recentsAnimationController.updateInputConsumerForApp(
@@ -584,47 +565,53 @@ final class InputMonitor {
if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
mDisableWallpaperTouchEvents = true; 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, // If there's a drag in progress and 'child' is a potential drop target,
// make sure it's been told about the drag // 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); mService.mDragDropController.sendDragStartedIfNeededLocked(w);
} }
populateInputWindowHandle(
inputWindowHandle, w, flags, type, isVisible, focusable, hasWallpaper);
// register key interception info // register key interception info
mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token, mService.mKeyInterceptionInfoForToken.put(w.mInputChannelToken,
w.getKeyInterceptionInfo()); w.getKeyInterceptionInfo());
if (w.mWinAnimator.hasSurface()) { if (w.mWinAnimator.hasSurface()) {
mInputTransaction.setInputWindowInfo( populateInputWindowHandle(inputWindowHandle, w);
setInputWindowInfoIfNeeded(mInputTransaction,
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); 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. // 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. // We need to check if any new field of InputWindowHandle could impact the result.
private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle, @VisibleForTesting
final String name, final int type, final boolean isVisible) { static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle,
inputWindowHandle.name = name; boolean isVisible) {
inputWindowHandle.layoutParamsType = type; inputWindowHandle.setDispatchingTimeoutMillis(0); // It should never receive input.
inputWindowHandle.dispatchingTimeoutMillis = 0; // it should never receive input inputWindowHandle.setVisible(isVisible);
inputWindowHandle.visible = isVisible; inputWindowHandle.setFocusable(false);
inputWindowHandle.focusable = false; inputWindowHandle.setInputFeatures(INPUT_FEATURE_NO_INPUT_CHANNEL);
inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL; // The input window handle without input channel must not have a token.
inputWindowHandle.scaleFactor = 1; inputWindowHandle.setToken(null);
inputWindowHandle.layoutParamsFlags = inputWindowHandle.setScaleFactor(1f);
FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE; inputWindowHandle.setLayoutParamsFlags(
inputWindowHandle.portalToDisplayId = INVALID_DISPLAY; FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE);
inputWindowHandle.touchableRegion.setEmpty(); inputWindowHandle.setPortalToDisplayId(INVALID_DISPLAY);
inputWindowHandle.clearTouchableRegion();
inputWindowHandle.setTouchableRegionCrop(null); inputWindowHandle.setTouchableRegionCrop(null);
inputWindowHandle.trustedOverlay = isTrustedOverlay(type);
} }
/** /**
@@ -635,9 +622,13 @@ final class InputMonitor {
*/ */
static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t, static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t,
int displayId, String name) { int displayId, String name) {
InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId); final InputWindowHandleWrapper inputWindowHandle = new InputWindowHandleWrapper(
populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true); new InputWindowHandle(null /* inputApplicationHandle */, displayId));
t.setInputWindowInfo(sc, inputWindowHandle); 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) { 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(), mClientChannel, mService.mAnimationHandler.getLooper(),
mService.mAnimator.getChoreographer()); mService.mAnimator.getChoreographer());
mDragApplicationHandle = new InputApplicationHandle(new Binder()); mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG,
mDragApplicationHandle.name = TAG; DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
displayContent.getDisplayId()); displayContent.getDisplayId());

View File

@@ -8365,7 +8365,7 @@ public class WindowManagerService extends IWindowManager.Stub
embeddedWindow.getName()); embeddedWindow.getName());
return; return;
} }
t.requestFocusTransfer(newFocusTarget.mInputWindowHandle.token, targetInputToken, t.requestFocusTransfer(newFocusTarget.mInputChannelToken, targetInputToken,
displayId).apply(); displayId).apply();
EventLog.writeEvent(LOGTAG_INPUT_FOCUS, EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Transfer focus request " + newFocusTarget, "Transfer focus request " + newFocusTarget,

View File

@@ -555,9 +555,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean mWindowRemovalAllowed; boolean mWindowRemovalAllowed;
// Input channel and input window handle used by the input dispatcher. // Input channel and input window handle used by the input dispatcher.
final InputWindowHandle mInputWindowHandle; final InputWindowHandleWrapper mInputWindowHandle;
InputChannel mInputChannel; 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() // Used to improve performance of toString()
private String mStringNameCache; private String mStringNameCache;
private CharSequence mLastTitle; private CharSequence mLastTitle;
@@ -855,6 +863,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
DeathRecipient deathRecipient = new DeathRecipient(); DeathRecipient deathRecipient = new DeathRecipient();
mPowerManagerWrapper = powerManagerWrapper; mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay; 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) { if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder() Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -870,7 +893,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mIsFloatingLayer = false; mIsFloatingLayer = false;
mBaseLayer = 0; mBaseLayer = 0;
mSubLayer = 0; mSubLayer = 0;
mInputWindowHandle = null;
mWinAnimator = null; mWinAnimator = null;
mWpcForDisplayConfigChanges = null; mWpcForDisplayConfigChanges = null;
return; return;
@@ -918,16 +940,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mLastRequestedWidth = 0; mLastRequestedWidth = 0;
mLastRequestedHeight = 0; mLastRequestedHeight = 0;
mLayer = 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 // Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged. // during onDisplayChanged.
@@ -1495,9 +1507,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} }
super.onDisplayChanged(dc); super.onDisplayChanged(dc);
// Window was not laid out for this display yet, so make sure mLayoutSeq does not match. // 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; mLayoutSeq = dc.mLayoutSeq - 1;
mInputWindowHandle.displayId = dc.getDisplayId(); mInputWindowHandle.setDisplayId(dc.getDisplayId());
} }
} }
@@ -2469,7 +2481,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} }
String name = getName(); String name = getName();
mInputChannel = mWmService.mInputManager.createInputChannel(name); mInputChannel = mWmService.mInputManager.createInputChannel(name);
mInputWindowHandle.token = mInputChannel.getToken(); mInputChannelToken = mInputChannel.getToken();
mInputWindowHandle.setToken(mInputChannelToken);
mWmService.mInputToWindowMap.put(mInputChannelToken, this);
if (outInputChannel != null) { if (outInputChannel != null) {
mInputChannel.copyTo(outInputChannel); mInputChannel.copyTo(outInputChannel);
} else { } else {
@@ -2478,7 +2492,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Create fake event receiver that simply reports all events as handled. // Create fake event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel); mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel);
} }
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
} }
void disposeInputChannel() { void disposeInputChannel() {
@@ -2486,17 +2499,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mDeadWindowEventReceiver.dispose(); mDeadWindowEventReceiver.dispose();
mDeadWindowEventReceiver = null; 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) { if (mInputChannel != null) {
mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
mInputChannel.dispose(); mInputChannel.dispose();
mInputChannel = null; mInputChannel = null;
} }
mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token); mInputWindowHandle.setToken(null);
mWmService.mInputToWindowMap.remove(mInputWindowHandle.token);
mInputWindowHandle.token = null;
} }
/** Returns true if the replacement window was removed. */ /** Returns true if the replacement window was removed. */
@@ -2566,11 +2581,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 boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
final Region region = inputWindowHandle.touchableRegion;
setTouchableRegionCropIfNeeded(inputWindowHandle);
if (modal) { if (modal) {
flags |= FLAG_NOT_TOUCH_MODAL; flags |= FLAG_NOT_TOUCH_MODAL;
if (mActivityRecord != null) { if (mActivityRecord != null) {
@@ -2592,7 +2604,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} }
// Translate to surface based coordinates. // 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- // 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- // scaling but the existing logic doesn't expect that. The result is that the already-
@@ -3441,7 +3456,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
break; break;
case TOUCHABLE_INSETS_REGION: { case TOUCHABLE_INSETS_REGION: {
outRegion.set(mGivenTouchableRegion); outRegion.set(mGivenTouchableRegion);
if (frame.left != 0 || frame.top != 0) {
outRegion.translate(frame.left, frame.top); outRegion.translate(frame.left, frame.top);
}
break; break;
} }
} }
@@ -3468,22 +3485,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) { private void cropRegionToStackBoundsIfNeeded(Region region) {
final Task task = getTask(); final Task task = getTask();
if (task == null || !task.cropWindowsToStackBounds()) { if (task == null || !task.cropWindowsToStackBounds()) {

View File

@@ -488,6 +488,9 @@ class WindowStateAnimator {
mSurfaceFormat = format; mSurfaceFormat = format;
w.setHasSurface(true); 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, ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
" CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s", " CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s",

View File

@@ -63,15 +63,18 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.graphics.Insets; import android.graphics.Insets;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.util.Size; import android.util.Size;
import android.view.DisplayCutout; import android.view.DisplayCutout;
import android.view.InputWindowHandle;
import android.view.InsetsState; import android.view.InsetsState;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.WindowManager; import android.view.WindowManager;
@@ -466,10 +469,10 @@ public class WindowStateTests extends WindowTestsBase {
public void testDisplayIdUpdatedOnReparent() { public void testDisplayIdUpdatedOnReparent() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
// fake a different display // fake a different display
app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1; app.mInputWindowHandle.setDisplayId(mDisplayContent.getDisplayId() + 1);
app.onDisplayChanged(mDisplayContent); app.onDisplayChanged(mDisplayContent);
assertThat(app.mInputWindowHandle.displayId, is(mDisplayContent.getDisplayId())); assertThat(app.mInputWindowHandle.getDisplayId(), is(mDisplayContent.getDisplayId()));
assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId())); assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId()));
} }
@@ -680,6 +683,54 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(win0.canReceiveTouchInput()); 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) @UseTestDisplay(addWindows = W_ACTIVITY)
@Test @Test
public void testNeedsRelativeLayeringToIme_notAttached() { public void testNeedsRelativeLayeringToIme_notAttached() {