diff options
6 files changed, 107 insertions, 10 deletions
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 3af8958368dc..67681dabf3f0 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -224,6 +224,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") @@ -3262,6 +3264,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 416f8372fd81..f2b4a1b2b032 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1541,10 +1541,25 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { return reinterpret_cast<jlong>(surfaceControl->getHandle().get()); } +static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj, + jobject toTokenObj, jobject focusedTokenObj, jint displayId) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + if (toTokenObj == NULL) return; + + sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj)); + sp<IBinder> focusedToken; + if (focusedTokenObj != NULL) { + focusedToken = ibinderForJavaObject(env, focusedTokenObj); + } + transaction->setFocusedWindow(toToken, focusedToken, systemTime(SYSTEM_TIME_MONOTONIC), + displayId); +} + // ---------------------------------------------------------------------------- // clang-format off 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", @@ -1721,7 +1736,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 }; // clang-format on diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index a2a2216028ac..e69319896762 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 8ccbd1166a44..77e573624b8a 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<WindowState> 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 4efd687b7bb4..c493b66b6204 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 6882dc4ca151..b50cb4c34398 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; } |