Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1a3b5b10 authored by Chavi Weingarten's avatar Chavi Weingarten
Browse files

Add focusTransferTarget in WindowInfo

A transfer focus request was originally added on FocusRequest object.
This meant when a host wanted to provide focus to an embedded window, it
would request to transfer focus, but provide its own token as the
focusedToken in the request. In FocusResolver, it would ensure that the
focusedToken was current focus before allow the request to proceed. This
worked in some cases, but created races where clients had to understand
timing in order to properly transfer focus.

Instead, use a persistent focusTransferTarget value set on the host to
transfer focus to the embedded when the host would have gained focus.

This solves a few issues with embedded windows.
1. Apps can request focus to the embedded by requesting focus to the SV
that hosts the embedded. However, if they request focus too early, it
will drop the request since the host has not yet gained focus. With
the current code, it will transfer focus to the embedded once the
host could have gained focus. If the host wants to revoke, the
focusTransferTarget can be set to null, which will give the host focus
again.

2. This fixes the issue if another window becomes focus. When WM gives
focus back to the host window, it should automatically give focus to
the embedded if it was last requested. The app shouldn't be required
to maintain the last state of the embedded focus to see if it needs
to transfer it again. It's actually not even possible once focus can
be given to embedded via touch since only WM and Input know about
this.

Additionally, ensure that tapping on the embedded window gives it focus.
This was added only for overlay layers, but is also needed for all
embedded windows. There's no way for the host to transfer focus when the
embedded is tapped since the events will go directly to the embedded
window and not to the host. If the embedded window is tapped, but the
host is not focused, we need to ensure WMS will bring that window to the
top and request focus to inputflinger. When the host focus request is
processed by FocusResolver, it will then give focus directly to the
embedded because of the focusTransferTarget set on the host WindowInfo.

Test: SurfaceControlViewHostTests
Bug: 230340812
Change-Id: If610d50e903fe8c437436e6efcb750a809274726
parent b5d7e0ed
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -158,6 +158,14 @@ public final class InputWindowHandle {
     */
    public Matrix transform;

    /**
     * The input token for the window to which focus should be transferred when this input window
     * can be successfully focused. If null, this input window will not transfer its focus to
     * any other window.
     */
    @Nullable
    public IBinder focusTransferTarget;

    private native void nativeDispose();

    public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
@@ -195,6 +203,7 @@ public final class InputWindowHandle {
            transform = new Matrix();
            transform.set(other.transform);
        }
        focusTransferTarget = other.focusTransferTarget;
    }

    @Override
+2 −23
Original line number Diff line number Diff line
@@ -265,7 +265,7 @@ public final class SurfaceControl implements Parcelable {
            int transformHint);
    private static native void nativeRemoveCurrentInputFocus(long nativeObject, int displayId);
    private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
            String windowName, IBinder focusedToken, String focusedWindowName, int displayId);
            String windowName, int displayId);
    private static native void nativeSetFrameTimelineVsync(long transactionObj,
            long frameTimelineVsyncId);
    private static native void nativeAddJankDataListener(long nativeListener,
@@ -3604,28 +3604,7 @@ public final class SurfaceControl implements Parcelable {
         */
        public Transaction setFocusedWindow(@NonNull IBinder token, String windowName,
                int displayId) {
            nativeSetFocusedWindow(mNativeObject, token,  windowName,
                    null /* focusedToken */, null /* focusedWindowName */, 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,
                                                String windowName,
                                                @NonNull IBinder focusedToken,
                                                String focusedWindowName,
                                                int displayId) {
            nativeSetFocusedWindow(mNativeObject, token, windowName, focusedToken,
                    focusedWindowName, displayId);
            nativeSetFocusedWindow(mNativeObject, token, windowName, displayId);
            return this;
        }

+15 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ static struct {
    WeakRefHandleField touchableRegionSurfaceControl;
    jfieldID transform;
    jfieldID windowToken;
    jfieldID focusTransferTarget;
} gInputWindowHandleClassInfo;

static struct {
@@ -216,6 +217,17 @@ bool NativeInputWindowHandle::updateInfo() {
        mInfo.windowToken.clear();
    }

    ScopedLocalRef<jobject>
            focusTransferTargetObj(env,
                                   env->GetObjectField(obj,
                                                       gInputWindowHandleClassInfo
                                                               .focusTransferTarget));
    if (focusTransferTargetObj.get()) {
        mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get());
    } else {
        mInfo.focusTransferTarget.clear();
    }

    env->DeleteLocalRef(obj);
    return true;
}
@@ -433,6 +445,9 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
    GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken",
                 "Landroid/os/IBinder;");

    GET_FIELD_ID(gInputWindowHandleClassInfo.focusTransferTarget, clazz, "focusTransferTarget",
                 "Landroid/os/IBinder;");

    jclass weakRefClazz;
    FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");

+2 −13
Original line number Diff line number Diff line
@@ -1820,17 +1820,11 @@ static void nativeRemoveCurrentInputFocus(JNIEnv* env, jclass clazz, jlong trans
}

static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
                                   jobject toTokenObj, jstring windowNameJstr,
                                   jobject focusedTokenObj, jstring focusedWindowNameJstr,
                                   jint displayId) {
                                   jobject toTokenObj, jstring windowNameJstr, 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);
    }

    FocusRequest request;
    request.token = toToken;
@@ -1839,11 +1833,6 @@ static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionO
        request.windowName = windowName.c_str();
    }

    request.focusedToken = focusedToken;
    if (focusedWindowNameJstr != NULL) {
        ScopedUtfChars focusedWindowName(env, focusedWindowNameJstr);
        request.focusedWindowName = focusedWindowName.c_str();
    }
    request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
    request.displayId = displayId;
    transaction->setFocusedWindow(request);
@@ -2236,7 +2225,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
            (void*)nativeGetHandle },
    {"nativeSetFixedTransformHint", "(JJI)V",
            (void*)nativeSetFixedTransformHint},
    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V",
    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;I)V",
            (void*)nativeSetFocusedWindow},
    {"nativeRemoveCurrentInputFocus", "(JI)V",
            (void*)nativeRemoveCurrentInputFocus},
+30 −38
Original line number Diff line number Diff line
@@ -99,23 +99,6 @@ class EmbeddedWindowController {
        }
    }

    WindowState getHostWindow(IBinder inputToken) {
        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
        return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
    }

    boolean isOverlay(IBinder inputToken) {
        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
        return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
    }

    void setIsOverlay(IBinder focusGrantToken) {
        EmbeddedWindow embeddedWindow = mWindowsByFocusToken.get(focusGrantToken);
        if (embeddedWindow != null) {
            embeddedWindow.setIsOverlay();
        }
    }

    void remove(IWindow client) {
        for (int i = mWindows.size() - 1; i >= 0; i--) {
            EmbeddedWindow ew = mWindows.valueAt(i);
@@ -176,14 +159,15 @@ class EmbeddedWindowController {
        public Session mSession;
        InputChannel mInputChannel;
        final int mWindowType;
        // Track whether the EmbeddedWindow is a system hosted overlay via
        // {@link OverlayHost}. In the case of client hosted overlays, the client
        // view hierarchy will take care of invoking requestEmbeddedWindowFocus
        // but for system hosted overlays we have to do this via tapOutsideDetection
        // and this variable is mostly used for tracking that.
        boolean mIsOverlay = false;

        private IBinder mFocusGrantToken;
        /**
         * A unique token associated with the embedded window that can be used by the host window
         * to request focus transfer to the embedded. This is not the input token since we don't
         * want to give clients access to each others input token.
         */
        private final IBinder mFocusGrantToken;

        private boolean mIsFocusable;

        /**
         * @param session  calling session to check ownership of the window
@@ -199,7 +183,8 @@ class EmbeddedWindowController {
         */
        EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
                       WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
                       int displayId, IBinder focusGrantToken, String inputHandleName) {
                       int displayId, IBinder focusGrantToken, String inputHandleName,
                       boolean isFocusable) {
            mSession = session;
            mWmService = service;
            mClient = clientToken;
@@ -214,6 +199,7 @@ class EmbeddedWindowController {
            final String hostWindowName =
                    (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
                            : "";
            mIsFocusable = isFocusable;
            mName = "Embedded{" + inputHandleName + hostWindowName + "}";
        }

@@ -279,13 +265,6 @@ class EmbeddedWindowController {
            return mOwnerUid;
        }

        void setIsOverlay() {
            mIsOverlay = true;
        }
        boolean getIsOverlay() {
            return mIsOverlay;
        }

        IBinder getFocusGrantToken() {
            return mFocusGrantToken;
        }
@@ -297,22 +276,35 @@ class EmbeddedWindowController {
            return null;
        }

        void setIsFocusable(boolean isFocusable) {
            mIsFocusable = isFocusable;
        }

        /**
         * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
         * so we need to participate inside handlePointerDownOutsideFocus logic
         * however client hosted overlays will rely on the hosting view hierarchy
         * to grant and revoke focus, and so the server side logic is not needed.
         * When an embedded window is touched when it's not currently focus, we need to switch
         * focus to that embedded window unless the embedded window was marked as not focusable.
         */
        @Override
        public boolean receiveFocusFromTapOutside() {
            return mIsOverlay;
            return mIsFocusable;
        }

        private void handleTap(boolean grantFocus) {
            if (mInputChannel != null) {
                if (mHostWindowState != null) {
                    mWmService.grantEmbeddedWindowFocus(mSession, mHostWindowState.mClient,
                            mFocusGrantToken, grantFocus);
                    if (grantFocus) {
                        // If granting focus to the embedded when tapped, we need to ensure the host
                        // gains focus as well or the transfer won't take effect since it requires
                        // the host to transfer the focus to the embedded.
                        mHostWindowState.handleTapOutsideFocusInsideSelf();
                    }
                } else {
                    mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
                }
            }
        }

        @Override
        public void handleTapOutsideFocusOutsideSelf() {
Loading