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

Commit 4c1ffefb authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Introduce ImeVisibilityStateComputer

With go/new-ime-visibility-control-u, this CL introduced following
classes to aim to improve our IME visiblity control protocal with
per-window state tracking:

- ImeVisibilityStateComputer:
  To compute the IME visibility state according the given WindowState
  from the focused window, or the app requested IME visibility
  from InputMethodManager.

- ImeVisibilityStateComputer.ImeTargetWindowState:
  Represents the current state of a window that focused by
  the Input Method

- ImeVisibilityStateComputer.ImeVisibilityPolicy:
  To manage all IME related visibility policies or configurations.

Note that the above classes is a preperation CL with only refactoring
& moving some checking IME visibility methods logic from IMMS side to
the computer, basically it should not have any behavior change.

Bug: 246309664
Test: atest CtsInputMethodTestCases
Change-Id: Id1115ceb951e4bb0361a32b824d966cc70b7d132
parent 8f0fc749
Loading
Loading
Loading
Loading
+351 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.inputmethod;

import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_EXPLICITLY_REQUESTED;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FORCED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.SoftInputModeFlags;

import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.IBinder;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.WindowManager;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;

import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerInternal;

import java.io.PrintWriter;
import java.util.WeakHashMap;

/**
 * A computer used by {@link InputMethodManagerService} that computes the IME visibility state
 * according the given {@link ImeTargetWindowState} from the focused window or the app requested IME
 * visibility from {@link InputMethodManager}.
 */
public final class ImeVisibilityStateComputer {

    private static final String TAG = "ImeVisibilityStateComputer";

    private static final boolean DEBUG = InputMethodManagerService.DEBUG;

    private final InputMethodManagerService mService;
    private final WindowManagerInternal mWindowManagerInternal;

    /**
     * A map used to track the requested IME target window and its state. The key represents the
     * token of the window and the value is the corresponding IME window state.
     */
    private final WeakHashMap<IBinder, ImeTargetWindowState> mRequestWindowStateMap =
            new WeakHashMap<>();

    /**
     * Set if IME was explicitly told to show the input method.
     *
     * @see InputMethodManager#SHOW_IMPLICIT that we set the value is {@code false}.
     * @see InputMethodManager#HIDE_IMPLICIT_ONLY that system will not hide IME when the value is
     * {@code true}.
     */
    boolean mRequestedShowExplicitly;

    /**
     * Set if we were forced to be shown.
     *
     * @see InputMethodManager#SHOW_FORCED
     * @see InputMethodManager#HIDE_NOT_ALWAYS
     */
    boolean mShowForced;

    /** Represent the invalid IME visibility state */
    public static final int STATE_INVALID = -1;

    /** State to handle hiding the IME window requested by the app. */
    public static final int STATE_HIDE_IME = 0;

    /** State to handle showing the IME window requested by the app. */
    public static final int STATE_SHOW_IME = 1;

    /** State to handle showing the IME window with making the overlay window above it.  */
    public static final int STATE_SHOW_IME_ABOVE_OVERLAY = 2;

    /** State to handle showing the IME window with making the overlay window behind it.  */
    public static final int STATE_SHOW_IME_BEHIND_OVERLAY = 3;

    /** State to handle showing an IME preview surface during the app was loosing the IME focus */
    public static final int STATE_SHOW_IME_SNAPSHOT = 4;
    @IntDef({
            STATE_INVALID,
            STATE_HIDE_IME,
            STATE_SHOW_IME,
            STATE_SHOW_IME_ABOVE_OVERLAY,
            STATE_SHOW_IME_BEHIND_OVERLAY,
            STATE_SHOW_IME_SNAPSHOT,
    })
    @interface VisibilityState {}

    /**
     * The policy to configure the IME visibility.
     */
    private final ImeVisibilityPolicy mPolicy;

    public ImeVisibilityStateComputer(InputMethodManagerService service) {
        mService = service;
        mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
        mPolicy = new ImeVisibilityPolicy();
    }

    /**
     * Called when {@link InputMethodManagerService} is processing the show IME request.
     * @param statsToken The token for tracking this show request
     * @param showFlags The additional operation flags to indicate whether this show request mode is
     *                  implicit or explicit.
     */
    void onImeShowFlags(int showFlags) {
        if ((showFlags & InputMethodManager.SHOW_FORCED) != 0) {
            mRequestedShowExplicitly = true;
            mShowForced = true;
        } else if ((showFlags & InputMethodManager.SHOW_IMPLICIT) == 0) {
            mRequestedShowExplicitly = true;
        }
    }

    /**
     * Called when {@link InputMethodManagerService} is processing the hide IME request.
     * @param statsToken The token for tracking this hide request
     * @param hideFlags The additional operation flags to indicate whether this hide request mode is
     *                  implicit or explicit.
     * @return {@code true} when the computer has proceed this hide request operations.
     */
    boolean canHideIme(@NonNull ImeTracker.Token statsToken, int hideFlags) {
        if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                && (mRequestedShowExplicitly || mShowForced)) {
            if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
            ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT);
            return false;
        }
        if (mShowForced && (hideFlags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
            if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
            ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
            return false;
        }
        ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
        return true;
    }

    int getImeShowFlags() {
        int flags = 0;
        if (mShowForced) {
            flags |= InputMethod.SHOW_FORCED | InputMethod.SHOW_EXPLICIT;
        } else if (mRequestedShowExplicitly) {
            flags |= InputMethod.SHOW_EXPLICIT;
        } else {
            flags |= InputMethodManager.SHOW_IMPLICIT;
        }
        return flags;
    }

    void clearImeShowFlags() {
        mRequestedShowExplicitly = false;
        mShowForced = false;
    }

    /**
     * Request to show/hide IME from the given window.
     *
     * @param windowToken The window which requests to show/hide IME.
     * @param showIme {@code true} means to show IME, {@code false} otherwise.
     *                            Note that in the computer will take this option to compute the
     *                            visibility state, it could be {@link #STATE_SHOW_IME} or
     *                            {@link #STATE_HIDE_IME}.
     */
    void requestImeVisibility(IBinder windowToken, boolean showIme) {
        final ImeTargetWindowState state = getOrCreateWindowState(windowToken);
        state.setRequestedImeVisible(showIme);
        setWindowState(windowToken, state);
    }

    ImeTargetWindowState getOrCreateWindowState(IBinder windowToken) {
        ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
        if (state == null) {
            state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNSPECIFIED, false, false);
        }
        return state;
    }

    ImeTargetWindowState getWindowStateOrNull(IBinder windowToken) {
        ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
        return state;
    }

    void setWindowState(IBinder windowToken, ImeTargetWindowState newState) {
        if (DEBUG) Slog.d(TAG, "setWindowState, windowToken=" + windowToken
                + ", state=" + newState);
        mRequestWindowStateMap.put(windowToken, newState);
    }

    IBinder getWindowTokenFrom(IBinder requestImeToken) {
        for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
            final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
            if (state.getRequestImeToken() == requestImeToken) {
                return windowToken;
            }
        }
        return null;
    }

    void dumpDebug(ProtoOutputStream proto, long fieldId) {
        proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly);
        proto.write(SHOW_FORCED, mShowForced);
    }

    void dump(PrintWriter pw) {
        final Printer p = new PrintWriterPrinter(pw);
        p.println(" mRequestedShowExplicitly=" + mRequestedShowExplicitly
                + " mShowForced=" + mShowForced);
    }

    /**
     * A settings class to manage all IME related visibility policies or settings.
     *
     * This is used for the visibility computer to manage and tell
     * {@link InputMethodManagerService} if the requested IME visibility is valid from
     * application call or the focus window.
     */
    static class ImeVisibilityPolicy {
        /**
         * {@code true} if the Ime policy has been set to
         * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}.
         *
         * This prevents the IME from showing when it otherwise may have shown.
         */
        private boolean mImeHiddenByDisplayPolicy;

        /**
         * Set when a11y requests to hide IME by A11yService#setShowMode(SHOW_MODE_HIDDEN)
         */
        private boolean mAccessibilityRequestingNoSoftKeyboard;

        void setImeHiddenByDisplayPolicy(boolean hideIme) {
            mImeHiddenByDisplayPolicy = hideIme;
        }

        void setA11yRequestNoSoftKeyboard(boolean a11yRequestNoIme) {
            mAccessibilityRequestingNoSoftKeyboard = a11yRequestNoIme;
        }
    }

    /**
     * A class that represents the current state of the IME target window.
     */
    static class ImeTargetWindowState {
        ImeTargetWindowState(@SoftInputModeFlags int softInputModeState, boolean imeFocusChanged,
                boolean hasFocusedEditor) {
            mSoftInputModeState = softInputModeState;
            mImeFocusChanged = imeFocusChanged;
            mHasFocusedEditor = hasFocusedEditor;
        }

        /**
         * Visibility state for this window. By default no state has been specified.
         */
        private final @SoftInputModeFlags int mSoftInputModeState;

        /**
         * {@code true} means the IME focus changed from the previous window, {@code false}
         * otherwise.
         */
        private final boolean mImeFocusChanged;

        /**
         * {@code true} when the window has focused an editor, {@code false} otherwise.
         */
        private final boolean mHasFocusedEditor;

        /**
         * Set if the client has asked for the input method to be shown.
         */
        private boolean mRequestedImeVisible;

        /**
         * A identifier for knowing the requester of {@link InputMethodManager#showSoftInput} or
         * {@link InputMethodManager#hideSoftInputFromWindow}.
         */
        private IBinder mRequestImeToken;

        /**
         * The IME target display id for which the latest startInput was called.
         */
        private int mImeDisplayId = DEFAULT_DISPLAY;

        boolean hasImeFocusChanged() {
            return mImeFocusChanged;
        }

        boolean hasEdiorFocused() {
            return mHasFocusedEditor;
        }

        int getSoftInputModeState() {
            return mSoftInputModeState;
        }

        private void setImeDisplayId(int imeDisplayId) {
            mImeDisplayId = imeDisplayId;
        }

        int getImeDisplayId() {
            return mImeDisplayId;
        }

        private void setRequestedImeVisible(boolean requestedImeVisible) {
            mRequestedImeVisible = requestedImeVisible;
        }

        boolean isRequestedImeVisible() {
            return mRequestedImeVisible;
        }

        void setRequestImeToken(IBinder token) {
            mRequestImeToken = token;
        }

        IBinder getRequestImeToken() {
            return mRequestImeToken;
        }

        @Override
        public String toString() {
            return "ImeTargetWindowState{ imeToken " + mRequestImeToken
                    + " imeFocusChanged " + mImeFocusChanged
                    + " hasEditorFocused " + mHasFocusedEditor
                    + " requestedImeVisible " + mRequestedImeVisible
                    + " imeDisplayId " + mImeDisplayId
                    + " softInputModeState " + softInputModeToString(mSoftInputModeState)
                    + "}";
        }
    }
}
+33 −72
Original line number Diff line number Diff line
@@ -39,8 +39,6 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.IN_FULLS
import static android.server.inputmethod.InputMethodManagerServiceProto.IS_INTERACTIVE;
import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_IME_TARGET_WINDOW_NAME;
import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_SWITCH_USER_ID;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_EXPLICITLY_REQUESTED;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FORCED;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_IME_WITH_HARD_KEYBOARD;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_REQUESTED;
import static android.server.inputmethod.InputMethodManagerServiceProto.SYSTEM_READY;
@@ -52,6 +50,7 @@ import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;

import static com.android.server.EventLogTags.IMF_HIDE_IME;
import static com.android.server.EventLogTags.IMF_SHOW_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
import static com.android.server.inputmethod.InputMethodUtils.isSoftInputModeStateVisibleAllowed;

@@ -126,7 +125,6 @@ import android.view.DisplayInfo;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManager.LayoutParams;
@@ -299,6 +297,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    @NonNull private final InputMethodBindingController mBindingController;
    @NonNull private final AutofillSuggestionsController mAutofillController;

    @GuardedBy("ImfLock.class")
    @NonNull private final ImeVisibilityStateComputer mVisibilityStateComputer;

    /**
     * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}.
     *
@@ -637,16 +638,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
     */
    private boolean mShowRequested;

    /**
     * Set if we were explicitly told to show the input method.
     */
    boolean mShowExplicitlyRequested;

    /**
     * Set if we were forced to be shown.
     */
    boolean mShowForced;

    /**
     * Set if we last told the input method to show itself.
     */
@@ -1747,6 +1738,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                        ? bindingControllerForTesting
                        : new InputMethodBindingController(this);
        mAutofillController = new AutofillSuggestionsController(this);

        mVisibilityStateComputer = new ImeVisibilityStateComputer(this);

        mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
                com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
        mNonPreemptibleInputMethods = mRes.getStringArray(
@@ -2339,29 +2333,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        mInputShown = false;
    }

    @GuardedBy("ImfLock.class")
    private int getImeShowFlagsLocked() {
        int flags = 0;
        if (mShowForced) {
            flags |= InputMethod.SHOW_FORCED
                    | InputMethod.SHOW_EXPLICIT;
        } else if (mShowExplicitlyRequested) {
            flags |= InputMethod.SHOW_EXPLICIT;
        }
        return flags;
    }

    @GuardedBy("ImfLock.class")
    private int getAppShowFlagsLocked() {
        int flags = 0;
        if (mShowForced) {
            flags |= InputMethodManager.SHOW_FORCED;
        } else if (!mShowExplicitlyRequested) {
            flags |= InputMethodManager.SHOW_IMPLICIT;
        }
        return flags;
    }

    @GuardedBy("ImfLock.class")
    @NonNull
    InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
@@ -2403,7 +2374,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            // Re-use current statsToken, if it exists.
            final ImeTracker.Token statsToken = mCurStatsToken;
            mCurStatsToken = null;
            showCurrentInputLocked(mCurFocusedWindow, statsToken, getAppShowFlagsLocked(),
            showCurrentInputLocked(mCurFocusedWindow, statsToken,
                    mVisibilityStateComputer.getImeShowFlags(),
                    null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
        }

@@ -3419,6 +3391,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    ImeTracker.ORIGIN_SERVER_START_INPUT, reason);
        }

        // TODO(b/246309664): make mShowRequested as per-window state.
        mShowRequested = true;
        if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) {
            ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
@@ -3426,19 +3399,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }
        ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);

        if ((flags & InputMethodManager.SHOW_FORCED) != 0) {
            mShowExplicitlyRequested = true;
            mShowForced = true;
        } else if ((flags & InputMethodManager.SHOW_IMPLICIT) == 0) {
            mShowExplicitlyRequested = true;
        }

        mVisibilityStateComputer.onImeShowFlags(flags);
        if (!mSystemReady) {
            ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_SYSTEM_READY);
            return false;
        }
        ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_SYSTEM_READY);

        mVisibilityStateComputer.requestImeVisibility(windowToken, true);

        // Ensure binding the connection when IME is going to show.
        mBindingController.setCurrentMethodVisible();
        final IInputMethodInvoker curMethod = getCurMethodLocked();
        if (curMethod != null) {
@@ -3448,7 +3418,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
            ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME);
            mCurStatsToken = null;
            final int showFlags = getImeShowFlagsLocked();
            final int showFlags = mVisibilityStateComputer.getImeShowFlags();
            if (DEBUG) {
                Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken
                        + ", " + showFlags + ", " + resultReceiver + ") for reason: "
@@ -3468,6 +3438,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                }
                onShowHideSoftInputRequested(true /* show */, windowToken, reason, statsToken);
            }
            // TODO(b/246309664): make mInputShown tracked by the Ime visibility computer.
            mInputShown = true;
            return true;
        } else {
@@ -3527,20 +3498,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason);
        }

        if ((flags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                && (mShowExplicitlyRequested || mShowForced)) {
            if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
            ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT);
        if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) {
            return false;
        }
        ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT);

        if (mShowForced && (flags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
            if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
            ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
            return false;
        }
        ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);

        // There is a chance that IMM#hideSoftInput() is called in a transient state where
        // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
@@ -3549,11 +3509,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        // application process as a valid request, and have even promised such a behavior with CTS
        // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
        // IMMS#InputShown indicates that the software keyboard is shown.
        // TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
        // TODO(b/246309664): Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
        IInputMethodInvoker curMethod = getCurMethodLocked();
        final boolean shouldHideSoftInput = (curMethod != null)
                && (mInputShown || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
        boolean res;

        mVisibilityStateComputer.requestImeVisibility(windowToken, false);
        if (shouldHideSoftInput) {
            final Binder hideInputToken = new Binder();
            mHideRequestWindowMap.put(hideInputToken, windowToken);
@@ -3578,20 +3539,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                }
                onShowHideSoftInputRequested(false /* show */, windowToken, reason, statsToken);
            }
            res = true;
        } else {
            ImeTracker.get().onCancelled(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE);
            res = false;
        }
        mBindingController.setCurrentMethodNotVisible();
        mVisibilityStateComputer.clearImeShowFlags();
        mInputShown = false;
        mShowRequested = false;
        mShowExplicitlyRequested = false;
        mShowForced = false;
        // Cancel existing statsToken for show IME as we got a hide request.
        ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
        mCurStatsToken = null;
        return res;
        return shouldHideSoftInput;
    }

    private boolean isImeClientFocused(IBinder windowToken, ClientState cs) {
@@ -3738,8 +3696,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        // In case mShowForced flag affects the next client to keep IME visible, when the current
        // client is leaving due to the next focused client, we clear mShowForced flag when the
        // next client's targetSdkVersion is T or higher.
        if (mCurFocusedWindow != windowToken && mShowForced && shouldClearFlag) {
            mShowForced = false;
        final boolean showForced = mVisibilityStateComputer.mShowForced;
        if (mCurFocusedWindow != windowToken && showForced && shouldClearFlag) {
            mVisibilityStateComputer.mShowForced = false;
        }

        // cross-profile access is always allowed here to allow profile-switching.
@@ -3763,6 +3722,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        final boolean startInputByWinGainedFocus =
                (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0;

        // Init the focused window state (e.g. whether the editor has focused or IME focus has
        // changed from another window).
        mVisibilityStateComputer.setWindowState(windowToken,
                new ImeTargetWindowState(softInputMode, !sameWindowFocused, isTextEditor));

        if (sameWindowFocused && isTextEditor) {
            if (DEBUG) {
                Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
@@ -4746,8 +4710,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            }
            proto.write(CUR_ID, getCurIdLocked());
            proto.write(SHOW_REQUESTED, mShowRequested);
            proto.write(SHOW_EXPLICITLY_REQUESTED, mShowExplicitlyRequested);
            proto.write(SHOW_FORCED, mShowForced);
            mVisibilityStateComputer.dumpDebug(proto, fieldId);
            proto.write(INPUT_SHOWN, mInputShown);
            proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode);
            proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked()));
@@ -5988,10 +5951,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            method = getCurMethodLocked();
            p.println("  mCurMethod=" + getCurMethodLocked());
            p.println("  mEnabledSession=" + mEnabledSession);
            p.println("  mShowRequested=" + mShowRequested
                    + " mShowExplicitlyRequested=" + mShowExplicitlyRequested
                    + " mShowForced=" + mShowForced
                    + " mInputShown=" + mInputShown);
            p.println("  mShowRequested=" + mShowRequested + " mInputShown=" + mInputShown);
            mVisibilityStateComputer.dump(pw);
            p.println("  mInFullscreenMode=" + mInFullscreenMode);
            p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
            p.println("  mSettingsObserver=" + mSettingsObserver);