From 3e5d102ef6f08b915f8f609cdbd9fa7a20e358b0 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Sat, 15 Aug 2020 15:56:20 -0700 Subject: [PATCH] Request focus using setFocusedWindow This change introduces two new SurfaceControl apis, one to request focus to a particular window and the other to transfer focus to an embedded window if the host window is currently focused. WindowHandleInfo is updated to specify focusability instead of the currently focused window. This will provide more information to InputDipatcher on what all the focusable windows are on screen and in the future allow it to determine a new focus target. Lastly, after updating all the WindowHandles in InputMonitor, we switch over to calling setFocusedWindow to specify the desired focused window. There should be no functional changes with this cl. Test: atest inputflinger_tests Test: atest CtsWindowManagerDeviceTestCases Test: atest FlickerTests Bug: 151179149 Change-Id: Iefda51ce73247b208115e64f9f8159a182d93ded --- core/java/android/view/SurfaceControl.java | 35 +++++++++++++++++ core/jni/android_view_SurfaceControl.cpp | 21 +++++++++- data/etc/services.core.protolog.json | 12 ++++++ .../com/android/server/wm/DisplayContent.java | 6 +++ .../com/android/server/wm/InputMonitor.java | 39 +++++++++++++++---- .../server/wm/RecentsAnimationController.java | 4 +- 6 files changed, 107 insertions(+), 10 deletions(-) diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 6ef086b55c41c..2bd3c06c318d9 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -221,6 +221,8 @@ public final class SurfaceControl implements Parcelable { private static native void nativeReleaseFrameRateFlexibilityToken(long token); private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject, int transformHint); + private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken, + IBinder focusedToken, int displayId); @Nullable @GuardedBy("mLock") @@ -3184,6 +3186,39 @@ public final class SurfaceControl implements Parcelable { } /** + * Sets focus on the window identified by the input {@code token} if the window is focusable + * otherwise the request is dropped. + * + * If the window is not visible, the request will be queued until the window becomes + * visible or the request is overrriden by another request. The currently focused window + * will lose focus immediately. This is to send the newly focused window any focus + * dispatched events that occur while it is completing its first draw. + * + * @hide + */ + public Transaction setFocusedWindow(@NonNull IBinder token, int displayId) { + nativeSetFocusedWindow(mNativeObject, token, null /* focusedToken */, displayId); + return this; + } + + /** + * Set focus on the window identified by the input {@code token} if the window identified by + * the input {@code focusedToken} is currently focused. If the {@code focusedToken} does not + * have focus, the request is dropped. + * + * This is used by forward focus transfer requests from clients that host embedded windows, + * and want to transfer focus to/from them. + * + * @hide + */ + public Transaction requestFocusTransfer(@NonNull IBinder token, + @NonNull IBinder focusedToken, + int displayId) { + nativeSetFocusedWindow(mNativeObject, token, focusedToken, displayId); + return this; + } + + /** * Merge the other transaction into this transaction, clearing the * other transaction as if it had been applied. * diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 85b4fe197980b..4ef15d7098ffd 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1505,9 +1505,24 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { return reinterpret_cast(surfaceControl->getHandle().get()); } +static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj, + jobject toTokenObj, jobject focusedTokenObj, jint displayId) { + auto transaction = reinterpret_cast(transactionObj); + if (toTokenObj == NULL) return; + + sp toToken(ibinderForJavaObject(env, toTokenObj)); + sp focusedToken; + if (focusedTokenObj != NULL) { + focusedToken = ibinderForJavaObject(env, focusedTokenObj); + } + transaction->setFocusedWindow(toToken, focusedToken, systemTime(SYSTEM_TIME_MONOTONIC), + displayId); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod sSurfaceControlMethods[] = { + // clang-format off {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J", (void*)nativeCreate }, {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", @@ -1686,7 +1701,11 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetGlobalShadowSettings }, {"nativeGetHandle", "(J)J", (void*)nativeGetHandle }, - {"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint}, + {"nativeSetFixedTransformHint", "(JJI)V", + (void*)nativeSetFixedTransformHint}, + {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Landroid/os/IBinder;I)V", + (void*)nativeSetFocusedWindow}, + // clang-format on }; int register_android_view_SurfaceControl(JNIEnv* env) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index a2a2216028ac4..e69319896762e 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1393,6 +1393,12 @@ "group": "WM_SHOW_SURFACE_ALLOC", "at": "com\/android\/server\/wm\/BlackFrame.java" }, + "155482615": { + "message": "Focus requested for window=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/InputMonitor.java" + }, "174572959": { "message": "DisplayArea info changed name=%s", "level": "VERBOSE", @@ -2635,6 +2641,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java" }, + "2081291430": { + "message": "Focus not requested for window=%s because it has no surface", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/InputMonitor.java" + }, "2083556954": { "message": "Set mOrientationChanging of %s", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index aa8069a76330b..b92adc075e0f6 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -618,6 +618,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private boolean mInEnsureActivitiesVisible = false; + /** + * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to + * prevent duplicate requests to input. + */ + WindowState mLastRequestedFocus = null; + private final Consumer mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final ActivityRecord activity = w.mActivityRecord; diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 4efd687b7bb40..c493b66b6204f 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -275,7 +275,7 @@ final class InputMonitor { void populateInputWindowHandle(final InputWindowHandle inputWindowHandle, final WindowState child, int flags, final int type, final boolean isVisible, - final boolean hasFocus, final boolean hasWallpaper) { + final boolean focusable, final boolean hasWallpaper) { // Add a window to our list of input windows. inputWindowHandle.name = child.toString(); flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags); @@ -283,7 +283,7 @@ final class InputMonitor { inputWindowHandle.layoutParamsType = type; inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis(); inputWindowHandle.visible = isVisible; - inputWindowHandle.focusable = hasFocus; + inputWindowHandle.focusable = focusable; inputWindowHandle.hasWallpaper = hasWallpaper; inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false; inputWindowHandle.ownerPid = child.mSession.mPid; @@ -472,8 +472,9 @@ final class InputMonitor { resetInputConsumers(mInputTransaction); - mDisplayContent.forAllWindows(this, - true /* traverseTopToBottom */); + mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); + + updateInputFocusRequest(); if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); @@ -483,6 +484,29 @@ final class InputMonitor { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + private void updateInputFocusRequest() { + if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) { + return; + } + + final WindowState focus = mDisplayContent.mCurrentFocus; + if (focus == null || focus.mInputWindowHandle.token == null) { + mDisplayContent.mLastRequestedFocus = focus; + return; + } + + if (!focus.mWinAnimator.hasSurface()) { + ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, + "Focus not requested for window=%s because it has no surface", + focus); + return; + } + + mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId); + mDisplayContent.mLastRequestedFocus = focus; + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus); + } + @Override public void accept(WindowState w) { final InputChannel inputChannel = w.mInputChannel; @@ -510,11 +534,12 @@ final class InputMonitor { final int flags = w.mAttrs.flags; final int privateFlags = w.mAttrs.privateFlags; - final boolean hasFocus = w.isFocused(); + final boolean focusable = w.canReceiveKeys() + && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop()); if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( - mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) { + mRecentsAnimationInputConsumer.mWindowHandle, focusable)) { mRecentsAnimationInputConsumer.show(mInputTransaction, w); mAddRecentsAnimationInputConsumerHandle = false; } @@ -559,7 +584,7 @@ final class InputMonitor { } populateInputWindowHandle( - inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper); + inputWindowHandle, w, flags, type, isVisible, focusable, hasWallpaper); // register key interception info mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token, diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 6882dc4ca1513..b50cb4c34398b 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -814,14 +814,14 @@ public class RecentsAnimationController implements DeathRecipient { } boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle, - boolean hasFocus) { + boolean focusable) { // Update the input consumer touchable region to match the target app main window final WindowState targetAppMainWindow = mTargetActivityRecord != null ? mTargetActivityRecord.findMainWindow() : null; if (targetAppMainWindow != null) { targetAppMainWindow.getBounds(mTmpRect); - inputWindowHandle.focusable = hasFocus; + inputWindowHandle.focusable = focusable; inputWindowHandle.touchableRegion.set(mTmpRect); return true; }