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

Commit 205da89c authored by Robert Carr's avatar Robert Carr Committed by Rob Carr
Browse files

IME Support for SurfaceControlViewHost

Since SurfaceControlViewHost embedded hierarchies can already receive
focus via grantEmbeddedWindowFocus, and ViewRoot can communicate
directly with IME service, most of the wiring is in place for IME
to work with SurfaceControlViewHost. It's mostly a few validation
checks around focus preventing us from doing this. To solve this we
make ImeInputTarget a "InputTarget" reference rather than a window
state reference. We extend InputTarget to allow for abstraction of
these few cases. At the moment we force embedded views to use
the remote insets target from SysUI, and they are not allowed
to control them themselves.

Bug: 213603716
Test: SurfaceControlViewHostTests
Change-Id: I7eec5bdf6798ef8cc5e6958987ca7b73b6881079
parent 60224bce
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -5083,10 +5083,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }

        if (!visible) {
            final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget(
                    DisplayContent.IME_TARGET_INPUT);
            mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null
                    && imeInputTarget.getWindow().mActivityRecord == this
            final InputTarget imeInputTarget = mDisplayContent.getImeInputTarget();
            mLastImeShown = imeInputTarget != null && imeInputTarget.getWindowState() != null
                    && imeInputTarget.getWindowState().mActivityRecord == this
                    && mDisplayContent.mInputMethodWindow != null
                    && mDisplayContent.mInputMethodWindow.isVisible();
            mImeInsetsFrozenUntilStartInput = true;
+34 −24
Original line number Diff line number Diff line
@@ -592,7 +592,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     * The window which receives input from the input method. This is also a candidate of the
     * input method control target.
     */
    private WindowState mImeInputTarget;
    private InputTarget mImeInputTarget;

    /**
     * The last ime input target processed from setImeLayeringTargetInner
     * this is to ensure we update the control target in the case when the IME
     * target changes while the IME layering target stays the same, for example
     * the case of the IME moving to a SurfaceControlViewHost backed EmbeddedWindow
     */
    private InputTarget mLastImeInputTarget;

    /**
     * This controls the visibility and animation of the input method window.
@@ -607,14 +615,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     */
    static final int IME_TARGET_LAYERING = 0;

    /**
     * Used by {@link #getImeTarget} to return the IME target which received the input connection
     * from IME.
     *
     * @see #mImeInputTarget
     */
    static final int IME_TARGET_INPUT = 1;

    /**
     * Used by {@link #getImeTarget} to return the IME target which controls the IME insets
     * visibility and animation.
@@ -625,7 +625,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp

    @IntDef(flag = false, prefix = { "IME_TARGET_" }, value = {
            IME_TARGET_LAYERING,
            IME_TARGET_INPUT,
            IME_TARGET_CONTROL,
    })
    @Retention(RetentionPolicy.SOURCE)
@@ -3314,7 +3313,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            mImeLayeringTarget.dumpDebug(proto, INPUT_METHOD_TARGET, logLevel);
        }
        if (mImeInputTarget != null) {
            mImeInputTarget.dumpDebug(proto, INPUT_METHOD_INPUT_TARGET, logLevel);
            mImeInputTarget.dumpProto(proto, INPUT_METHOD_INPUT_TARGET, logLevel);
        }
        if (mImeControlTarget != null
                && mImeControlTarget.getWindow() != null) {
@@ -3877,7 +3876,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    }

    private boolean isImeControlledByApp() {
        return mImeInputTarget != null && !mImeInputTarget.inMultiWindowMode();
        return mImeInputTarget != null && mImeInputTarget.shouldControlIme();
    }

    boolean shouldImeAttachedToApp() {
@@ -3940,19 +3939,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     *
     * @param type The type of the IME target.
     * @see #IME_TARGET_LAYERING
     * @see #IME_TARGET_INPUT
     * @see #IME_TARGET_CONTROL
     */
    InsetsControlTarget getImeTarget(@InputMethodTarget int type) {
        switch (type) {
            case IME_TARGET_LAYERING: return mImeLayeringTarget;
            case IME_TARGET_INPUT: return mImeInputTarget;
            case IME_TARGET_CONTROL: return mImeControlTarget;
            default:
                return null;
        }
    }

    InputTarget getImeInputTarget() {
        return mImeInputTarget;
    }

    // IMPORTANT: When introducing new dependencies in this method, make sure that
    // changes to those result in RootWindowContainer.updateDisplayImePolicyCache()
    // being called.
@@ -4001,9 +4002,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     *               placed at its parent's surface.
     */
    private void setImeLayeringTargetInner(@Nullable WindowState target) {
        if (target == mImeLayeringTarget) {
        /**
         * This function is also responsible for updating the IME control target
         * and so in the case where the IME layering target does not change
         * but the Input target does (for example, IME moving to a SurfaceControlViewHost
         * we have to continue executing this function, otherwise there is no work
         * to do.
         */
        if (target == mImeLayeringTarget && mLastImeInputTarget == mImeInputTarget) {
            return;
        }
        mLastImeInputTarget = mImeInputTarget;

        // If the IME target is the input target, before it changes, prepare the IME screenshot
        // for the last IME target when its task is applying app transition. This is for the
        // better IME transition to keep IME visibility when transitioning to the next task.
@@ -4045,9 +4055,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    }

    @VisibleForTesting
    void setImeInputTarget(WindowState target) {
    void setImeInputTarget(InputTarget target) {
        mImeInputTarget = target;
        boolean canScreenshot = mImeInputTarget == null || !mImeInputTarget.isSecureLocked();
        boolean canScreenshot = mImeInputTarget == null || mImeInputTarget.canScreenshotIme();
        if (mImeWindowsContainer.setCanScreenshot(canScreenshot)) {
            mWmService.requestTraversal();
        }
@@ -4163,7 +4173,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     * The IME input target is the window which receives input from IME. It is also a candidate
     * which controls the visibility and animation of the input method window.
     */
    void updateImeInputAndControlTarget(WindowState target) {
    void updateImeInputAndControlTarget(InputTarget target) {
        if (mImeInputTarget != target) {
            ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
            setImeInputTarget(target);
@@ -4173,8 +4183,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        }
        // Unfreeze IME insets after the new target updated, in case updateAboveInsetsState may
        // deliver unrelated IME insets change to the non-IME requester.
        if (target != null && target.mActivityRecord != null) {
            target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
        if (target != null) {
            target.unfreezeInsetsAfterStartInput();
        }
    }

@@ -4232,11 +4242,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    InsetsControlTarget computeImeControlTarget() {
        if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null
                || (mImeInputTarget != null
                        && getImeHostOrFallback(mImeInputTarget.getWindow())
                        && getImeHostOrFallback(mImeInputTarget.getWindowState())
                               == mRemoteInsetsControlTarget)) {
            return mRemoteInsetsControlTarget;
        } else {
            return mImeInputTarget;
            return mImeInputTarget != null ? mImeInputTarget.getWindowState() : null;
        }
    }

@@ -4249,7 +4259,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        // screen. If it's not covering the entire screen the IME might extend beyond the apps
        // bounds.
        if (shouldImeAttachedToApp()) {
            if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.mActivityRecord) {
            if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) {
                // Do not change parent if the window hasn't requested IME.
                return null;
            }
+68 −0
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.server.wm;


import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

@@ -25,6 +28,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.proto.ProtoOutputStream;
import android.util.Slog;
import android.view.IWindow;
import android.view.InputApplicationHandle;
@@ -43,6 +47,8 @@ class EmbeddedWindowController {
    private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
    private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken =
        new ArrayMap<>();
    private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken =
        new ArrayMap<>();
    private final Object mGlobalLock;
    private final ActivityTaskManagerService mAtmService;

@@ -63,6 +69,7 @@ class EmbeddedWindowController {
            mWindows.put(inputToken, window);
            final IBinder focusToken = window.getFocusGrantToken();
            mWindowsByFocusToken.put(focusToken, window);
            mWindowsByWindowToken.put(window.getWindowToken(), window);
            updateProcessController(window);
            window.mClient.asBinder().linkToDeath(()-> {
                synchronized (mGlobalLock) {
@@ -116,6 +123,7 @@ class EmbeddedWindowController {
            if (ew.mClient.asBinder() == client.asBinder()) {
                mWindows.removeAt(i).onRemoved();
                mWindowsByFocusToken.remove(ew.getFocusGrantToken());
                mWindowsByWindowToken.remove(ew.getWindowToken());
                return;
            }
        }
@@ -127,6 +135,7 @@ class EmbeddedWindowController {
            if (ew.mHostWindowState == host) {
                mWindows.removeAt(i).onRemoved();
                mWindowsByFocusToken.remove(ew.getFocusGrantToken());
                mWindowsByWindowToken.remove(ew.getWindowToken());
            }
        }
    }
@@ -139,6 +148,10 @@ class EmbeddedWindowController {
        return mWindowsByFocusToken.get(focusGrantToken);
    }

    EmbeddedWindow getByWindowToken(IBinder windowToken) {
        return mWindowsByWindowToken.get(windowToken);
    }

    void onActivityRemoved(ActivityRecord activityRecord) {
        for (int i = mWindows.size() - 1; i >= 0; i--) {
            final EmbeddedWindow window = mWindows.valueAt(i);
@@ -243,16 +256,30 @@ class EmbeddedWindowController {
            return mDisplayId;
        }

        @Override
        public DisplayContent getDisplayContent() {
            return mWmService.mRoot.getDisplayContent(getDisplayId());
        }

        @Override
        public IWindow getIWindow() {
            return mClient;
        }

        public IBinder getWindowToken() {
            return mClient.asBinder();
        }

        @Override
        public int getPid() {
            return mOwnerPid;
        }

        @Override
        public int getUid() {
            return mOwnerUid;
        }

        void setIsOverlay() {
            mIsOverlay = true;
        }
@@ -297,5 +324,46 @@ class EmbeddedWindowController {
        public void handleTapOutsideFocusInsideSelf() {
            handleTap(true);
        }

        @Override
        public boolean shouldControlIme() {
            return false;
        }

        @Override
        public boolean canScreenshotIme() {
            return true;
        }

        @Override
        public void unfreezeInsetsAfterStartInput() {
        }

        @Override
        public InsetsControlTarget getImeControlTarget() {
            return mWmService.getDefaultDisplayContentLocked().mRemoteInsetsControlTarget;
        }

        @Override
        public boolean isInputMethodClientFocus(int uid, int pid) {
            return uid == mOwnerUid && pid == mOwnerPid;
        }

        @Override
        public ActivityRecord getActivityRecord() {
            return null;
        }

        @Override
        public void dumpProto(ProtoOutputStream proto, long fieldId,
                              @WindowTraceLogLevel int logLevel) {
            final long token = proto.start(fieldId);

            final long token2 = proto.start(IDENTIFIER);
            proto.write(HASH_CODE, System.identityHashCode(this));
            proto.write(TITLE, "EmbeddedWindow");
            proto.end(token2);
            proto.end(token);
        }
    }
}
+1 −2
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.view.InsetsState.ITYPE_IME;

import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
@@ -249,7 +248,7 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
    }

    private boolean isImeInputTarget(InsetsControlTarget target) {
        return target == mDisplayContent.getImeTarget(IME_TARGET_INPUT);
        return target == mDisplayContent.getImeInputTarget();
    }

    private boolean sameAsImeControlTarget() {
+20 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.wm;

import android.view.IWindow;
import android.util.proto.ProtoOutputStream;

/**
 * Common interface between focusable objects.
@@ -36,6 +37,7 @@ interface InputTarget {

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

    /**
     * Indicates whether a target should receive focus from server side
@@ -45,7 +47,25 @@ interface InputTarget {
     */
    boolean receiveFocusFromTapOutside();

    // Gaining focus
    void handleTapOutsideFocusInsideSelf();
    // Losing focus
    void handleTapOutsideFocusOutsideSelf();

    // Whether this input target can control the IME itself
    boolean shouldControlIme();
    // Whether this input target can be screenshoted by the IME system
    boolean canScreenshotIme();

    ActivityRecord getActivityRecord();
    void unfreezeInsetsAfterStartInput();

    boolean isInputMethodClientFocus(int uid, int pid);

    DisplayContent getDisplayContent();
    InsetsControlTarget getImeControlTarget();

    void dumpProto(ProtoOutputStream proto, long fieldId,
                   @WindowTraceLogLevel int logLevel);
}
Loading