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

Commit 47857a0a authored by Robert Carr's avatar Robert Carr Committed by Rob Carr
Browse files

WindowManager: Support focus for SurfacePackage overlays

We already support granting focus to SurfaceControlViewHost
embedded windows. In the normal path where the SurfacePackage
is embedded in a client view hierarchy, we rely on the client
to invoke grantEmbeddedWindowFocus(true/false) as the SurfaceView
hosting the SurfacePackage moves in and out of focus according
to client side state. For server hosted overlays, we can reuse
grantEmbeddedWindowFocus but we need someone to call it. To accomplish
this we reorganize the logic in handlePointerDownOutsideFocus to
work in terms of InputTarget instead of WindowState, and graft in
the appropriate logic for EmbeddedWindows inside
EmbeddedWindowController.java.

Bug: 214239638
Test: Existing tests pass
Change-Id: Ie91b8cf790b4ab2092a90e310e7bd200a4adcff4
parent 87945271
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
@@ -93,6 +93,18 @@ class EmbeddedWindowController {
        return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
    }

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

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

    void remove(IWindow client) {
        for (int i = mWindows.size() - 1; i >= 0; i--) {
            if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) {
@@ -138,6 +150,12 @@ 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;

        /**
         * @param session  calling session to check ownership of the window
@@ -216,5 +234,39 @@ class EmbeddedWindowController {
        public int getPid() {
            return mOwnerPid;
        }

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

        /**
         * 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.
         */
        @Override
        public boolean receiveFocusFromTapOutside() {
            return mIsOverlay;
        }

        private void handleTap(boolean grantFocus) {
            if (mInputChannel != null) {
                mWmService.grantEmbeddedWindowFocus(mSession, mInputChannel.getToken(), grantFocus);
            }
        }

        @Override
        public void handleTapOutsideFocusOutsideSelf() {
            handleTap(false);
        }

        @Override
        public void handleTapOutsideFocusInsideSelf() {
            handleTap(true);
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -36,5 +36,16 @@ interface InputTarget {

    /* Owning pid of the target. */
    int getPid();

    /**
     * Indicates whether a target should receive focus from server side
     * tap outside focus detection. For example, this is false in the case of
     * EmbeddedWindows in a client view hierarchy, where the client will do internal
     * tap detection and invoke grantEmbeddedWindowFocus itself
     */
    boolean receiveFocusFromTapOutside();

    void handleTapOutsideFocusInsideSelf();
    void handleTapOutsideFocusOutsideSelf();
}
+2 −0
Original line number Diff line number Diff line
@@ -74,6 +74,8 @@ class OverlayHost {
        requireOverlaySurfaceControl();
        mOverlays.add(p);

        mWmService.mEmbeddedWindowController.setIsOverlay(p.getInputToken());

        SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
        t.reparent(p.getSurfaceControl(), mSurfaceControl)
            .show(p.getSurfaceControl());
+11 −16
Original line number Diff line number Diff line
@@ -738,6 +738,8 @@ public class WindowManagerService extends IWindowManager.Stub
    final WindowContextListenerController mWindowContextListenerController =
            new WindowContextListenerController();

    private InputTarget mFocusedInputTarget;

    @VisibleForTesting
    final class SettingsObserver extends ContentObserver {
        private final Uri mDisplayInversionEnabledUri =
@@ -5011,6 +5013,7 @@ public class WindowManagerService extends IWindowManager.Stub
                Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
                return;
            }
            mFocusedInputTarget = newTarget;

            mAccessibilityController.onFocusChanged(lastTarget, newTarget);
            ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
@@ -8170,21 +8173,14 @@ public class WindowManagerService extends IWindowManager.Stub
    }

    private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
        WindowState touchedWindow = mInputToWindowMap.get(touchedToken);
        if (touchedWindow == null) {
            // if a user taps outside the currently focused window onto an embedded window, treat
            // it as if the host window was tapped.
            touchedWindow = mEmbeddedWindowController.getHostWindow(touchedToken);
        }

        if (touchedWindow == null || !touchedWindow.canReceiveKeys(true /* fromUserTouch */)) {
        InputTarget t = getInputTargetFromToken(touchedToken);
        if (t == null || !t.receiveFocusFromTapOutside()) {
            // If the window that received the input event cannot receive keys, don't move the
            // display it's on to the top since that window won't be able to get focus anyway.
            return;
        }

        if (mRecentsAnimationController != null
                && mRecentsAnimationController.getTargetAppMainWindow() == touchedWindow) {
            && mRecentsAnimationController.getTargetAppMainWindow() == t) {
            // If there is an active recents animation and touched window is the target, then ignore
            // the touch. The target already handles touches using its own input monitor and we
            // don't want to trigger any lifecycle changes from focusing another window.
@@ -8194,13 +8190,11 @@ public class WindowManagerService extends IWindowManager.Stub
        }

        ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s",
                touchedWindow);
        final DisplayContent displayContent = touchedWindow.getDisplayContent();
        if (!displayContent.isOnTop()) {
            displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
                    true /* includingParents */);
                t);
        if (mFocusedInputTarget != t && mFocusedInputTarget != null) {
            mFocusedInputTarget.handleTapOutsideFocusOutsideSelf();
        }
        handleTaskFocusChange(touchedWindow.getTask(), touchedWindow.mActivityRecord);
        t.handleTapOutsideFocusInsideSelf();
    }

    @VisibleForTesting
@@ -8777,4 +8771,5 @@ public class WindowManagerService extends IWindowManager.Stub

        mTaskTransitionSpec = null;
    }

}
+20 −0
Original line number Diff line number Diff line
@@ -5985,4 +5985,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
    boolean isTrustedOverlay() {
        return mInputWindowHandle.isTrustedOverlay();
    }

    public boolean receiveFocusFromTapOutside() {
        return canReceiveKeys(true);
    }

    @Override
    public void handleTapOutsideFocusOutsideSelf() {
        // Nothing to do here since raising the other window will naturally take care of
        // us loosing focus
    }

    @Override
    public void handleTapOutsideFocusInsideSelf() {
        final DisplayContent displayContent = getDisplayContent();
        if (!displayContent.isOnTop()) {
            displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
                    true /* includingParents */);
        }
        mWmService.handleTaskFocusChange(getTask(), mActivityRecord);
    }
}