From 4c1ffefb6a5a9f450eb22011566818a15b3e01b6 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Sun, 18 Sep 2022 21:43:17 +0800 Subject: [PATCH 1/6] 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 --- .../ImeVisibilityStateComputer.java | 351 ++++++++++++++++++ .../InputMethodManagerService.java | 105 ++---- 2 files changed, 384 insertions(+), 72 deletions(-) create mode 100644 services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java new file mode 100644 index 0000000000000..240fb659c8947 --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -0,0 +1,351 @@ +/* + * 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 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) + + "}"; + } + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 5b9a6639bff6d..1dc6a44c322bb 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -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); From baebf0fecf959b93981fbf43e6c7fa156cdbdbea Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Sun, 18 Sep 2022 21:34:01 +0800 Subject: [PATCH 2/6] Introduce ImeVisibilityApplier With go/new-ime-visibility-control-u, this CL introduced ImeVisibilityApplier interface to abstract the implementation of applying IME visibility with adjusting IME z-ordering for aiming to stablize IME z-ordering control. Note that this is the first CL with - Refactoring part of IMMS#{show, hide}CurrentInputLocked logic to a default implementation class of ImeVisibilityAppler - Clean-up IMMS#{mShowRequestWindowMap, mHideRequestWindowMap} with replaced by ImeVisibilityComputer.WindowState#setRequestImeToken Will keep update follow-up CLs for clean-up applying IME visiblity and adjusting IME z-ordering stuffs. Bug: 246309664 Test: atest CtsInputMethodTestCases Change-Id: I410a29ce4a4e27b2ffa66c9d090d969eb43d0e36 Change-Id: I12d99c15ae0d8965a21406d2495ce5cb18afaea0 --- .../DefaultImeVisibilityApplier.java | 114 ++++++++++++++++++ .../inputmethod/ImeVisibilityApplier.java | 80 ++++++++++++ .../ImeVisibilityStateComputer.java | 11 +- .../InputMethodManagerService.java | 83 +++---------- 4 files changed, 222 insertions(+), 66 deletions(-) create mode 100644 services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java create mode 100644 services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java new file mode 100644 index 0000000000000..c85f151cfece4 --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -0,0 +1,114 @@ +/* + * 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.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 android.annotation.Nullable; +import android.os.Binder; +import android.os.IBinder; +import android.os.ResultReceiver; +import android.util.EventLog; +import android.util.Slog; +import android.view.inputmethod.ImeTracker; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.inputmethod.InputMethodDebug; +import com.android.internal.inputmethod.SoftInputShowHideReason; + +import java.util.Objects; + +/** + * The default implementation of {@link ImeVisibilityApplier} used in + * {@link InputMethodManagerService}. + */ +final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { + + private static final String TAG = "DefaultImeVisibilityApplier"; + + private static final boolean DEBUG = InputMethodManagerService.DEBUG; + + private InputMethodManagerService mService; + + DefaultImeVisibilityApplier(InputMethodManagerService service) { + mService = service; + } + + @GuardedBy("ImfLock.class") + @Override + public void performShowIme(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { + final IInputMethodInvoker curMethod = mService.getCurMethodLocked(); + if (curMethod != null) { + // create a placeholder token for IMS so that IMS cannot inject windows into client app. + final IBinder showInputToken = new Binder(); + mService.setRequestImeTokenToWindow(windowToken, showInputToken); + if (DEBUG) { + Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken + + ", " + showFlags + ", " + resultReceiver + ") for reason: " + + InputMethodDebug.softInputDisplayReasonToString(reason)); + } + // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. + if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) { + if (DEBUG_IME_VISIBILITY) { + EventLog.writeEvent(IMF_SHOW_IME, statsToken.getTag(), + Objects.toString(mService.mCurFocusedWindow), + InputMethodDebug.softInputDisplayReasonToString(reason), + InputMethodDebug.softInputModeToString( + mService.mCurFocusedWindowSoftInputMode)); + } + mService.onShowHideSoftInputRequested(true /* show */, windowToken, reason, + statsToken); + } + } + } + + @GuardedBy("ImfLock.class") + @Override + public void performHideIme(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { + final IInputMethodInvoker curMethod = mService.getCurMethodLocked(); + if (curMethod != null) { + final Binder hideInputToken = new Binder(); + mService.setRequestImeTokenToWindow(windowToken, hideInputToken); + // The IME will report its visible state again after the following message finally + // delivered to the IME process as an IPC. Hence the inconsistency between + // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in + // the final state. + if (DEBUG) { + Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken + + ", " + resultReceiver + ") for reason: " + + InputMethodDebug.softInputDisplayReasonToString(reason)); + } + // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. + if (curMethod.hideSoftInput(hideInputToken, statsToken, 0, resultReceiver)) { + if (DEBUG_IME_VISIBILITY) { + EventLog.writeEvent(IMF_HIDE_IME, statsToken.getTag(), + Objects.toString(mService.mCurFocusedWindow), + InputMethodDebug.softInputDisplayReasonToString(reason), + InputMethodDebug.softInputModeToString( + mService.mCurFocusedWindowSoftInputMode)); + } + mService.onShowHideSoftInputRequested(false /* show */, windowToken, reason, + statsToken); + } + } + } +} diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java new file mode 100644 index 0000000000000..f398864f3b07f --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -0,0 +1,80 @@ +/* + * 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 android.annotation.Nullable; +import android.os.IBinder; +import android.os.ResultReceiver; +import android.view.inputmethod.ImeTracker; + +import com.android.internal.inputmethod.SoftInputShowHideReason; + +/** + * Interface for IME visibility operations like show/hide and update Z-ordering relative to the IME + * targeted window. + */ +interface ImeVisibilityApplier { + /** + * Performs showing IME on top of the given window. + * + * @param windowToken The token of a window that currently has focus. + * @param statsToken A token that tracks the progress of an IME request. + * @param showFlags Provides additional operating flags to show IME. + * @param resultReceiver If non-null, this will be called back to the caller when + * it has processed request to tell what it has done. + * @param reason The reason for requesting to show IME. + */ + default void performShowIme(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {} + + /** + * Performs hiding IME to the given window + * + * @param windowToken The token of a window that currently has focus. + * @param statsToken A token that tracks the progress of an IME request. + * @param resultReceiver If non-null, this will be called back to the caller when + * it has processed request to tell what it has done. + * @param reason The reason for requesting to hide IME. + */ + default void performHideIme(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {} + + /** + * Applies the IME visibility from {@link android.inputmethodservice.InputMethodService} with + * according to the given visibility state. + * + * @param windowToken The token of a window for applying the IME visibility + * @param state The new IME visibility state for the applier to handle + */ + default void applyImeVisibility(IBinder windowToken, + @ImeVisibilityStateComputer.VisibilityState int state) { + // TODO: migrate IMMS#applyImeVisibility logic to here. + } + + /** + * Updates the IME Z-ordering relative to the given window. + * + * This used to adjust the IME relative layer of the window during + * {@link InputMethodManagerService} is in switching IME clients. + * + * @param windowToken The token of a window to update the Z-ordering relative to the IME. + */ + default void updateImeLayeringByTarget(IBinder windowToken) { + // TODO: add a method in WindowManagerInternal to call DC#updateImeInputAndControlTarget + // here to end up updating IME layering after IMMS#attachNewInputLocked called. + } +} diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 240fb659c8947..2374995843002 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -201,6 +201,14 @@ public final class ImeVisibilityStateComputer { return state; } + void setRequestImeTokenToWindow(IBinder windowToken, IBinder token) { + ImeTargetWindowState state = getWindowStateOrNull(windowToken); + if (state != null) { + state.setRequestImeToken(token); + setWindowState(windowToken, state); + } + } + void setWindowState(IBinder windowToken, ImeTargetWindowState newState) { if (DEBUG) Slog.d(TAG, "setWindowState, windowToken=" + windowToken + ", state=" + newState); @@ -214,7 +222,8 @@ public final class ImeVisibilityStateComputer { return windowToken; } } - return null; + // Fallback to the focused window for some edge cases (e.g. relaunching the activity) + return mService.mCurFocusedWindow; } void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1dc6a44c322bb..5ca08b878f9d4 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -46,10 +46,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; -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; @@ -300,6 +297,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") @NonNull private final ImeVisibilityStateComputer mVisibilityStateComputer; + @GuardedBy("ImfLock.class") + @NonNull private final DefaultImeVisibilityApplier mVisibilityApplier; + /** * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}. * @@ -964,22 +964,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } - /** - * Map of generated token to windowToken that is requesting - * {@link InputMethodManager#showSoftInput(View, int)}. - * This map tracks origin of showSoftInput requests. - */ - @GuardedBy("ImfLock.class") - private final WeakHashMap mShowRequestWindowMap = new WeakHashMap<>(); - - /** - * Map of generated token to windowToken that is requesting - * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}. - * This map tracks origin of hideSoftInput requests. - */ - @GuardedBy("ImfLock.class") - private final WeakHashMap mHideRequestWindowMap = new WeakHashMap<>(); - /** * A ring buffer to store the history of {@link StartInputInfo}. */ @@ -1740,6 +1724,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mAutofillController = new AutofillSuggestionsController(this); mVisibilityStateComputer = new ImeVisibilityStateComputer(this); + mVisibilityApplier = new DefaultImeVisibilityApplier(this); mPreventImeStartupUnlessTextEditor = mRes.getBoolean( com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor); @@ -3357,6 +3342,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } + @GuardedBy("ImfLock.class") + void setRequestImeTokenToWindow(IBinder windowToken, IBinder token) { + mVisibilityStateComputer.setRequestImeTokenToWindow(windowToken, token); + } + @BinderThread @Override public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) { @@ -3411,38 +3401,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Ensure binding the connection when IME is going to show. mBindingController.setCurrentMethodVisible(); final IInputMethodInvoker curMethod = getCurMethodLocked(); + ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); if (curMethod != null) { - // create a placeholder token for IMS so that IMS cannot inject windows into client app. - Binder showInputToken = new Binder(); - mShowRequestWindowMap.put(showInputToken, windowToken); - ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME); mCurStatsToken = null; - final int showFlags = mVisibilityStateComputer.getImeShowFlags(); - if (DEBUG) { - Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken - + ", " + showFlags + ", " + resultReceiver + ") for reason: " - + InputMethodDebug.softInputDisplayReasonToString(reason)); - } if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) { curMethod.updateEditorToolType(lastClickToolType); } - // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. - if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) { - if (DEBUG_IME_VISIBILITY) { - EventLog.writeEvent(IMF_SHOW_IME, statsToken.getTag(), - Objects.toString(mCurFocusedWindow), - InputMethodDebug.softInputDisplayReasonToString(reason), - InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)); - } - onShowHideSoftInputRequested(true /* show */, windowToken, reason, statsToken); - } + mVisibilityApplier.performShowIme(windowToken, statsToken, + mVisibilityStateComputer.getImeShowFlags(), resultReceiver, reason); // TODO(b/246309664): make mInputShown tracked by the Ime visibility computer. mInputShown = true; return true; } else { - ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_WAIT_IME); mCurStatsToken = statsToken; } @@ -3516,29 +3488,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mVisibilityStateComputer.requestImeVisibility(windowToken, false); if (shouldHideSoftInput) { - final Binder hideInputToken = new Binder(); - mHideRequestWindowMap.put(hideInputToken, windowToken); // The IME will report its visible state again after the following message finally // delivered to the IME process as an IPC. Hence the inconsistency between // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in // the final state. ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE); - if (DEBUG) { - Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken - + ", " + resultReceiver + ") for reason: " - + InputMethodDebug.softInputDisplayReasonToString(reason)); - } - // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. - if (curMethod.hideSoftInput(hideInputToken, statsToken, 0 /* flags */, - resultReceiver)) { - if (DEBUG_IME_VISIBILITY) { - EventLog.writeEvent(IMF_HIDE_IME, statsToken.getTag(), - Objects.toString(mCurFocusedWindow), - InputMethodDebug.softInputDisplayReasonToString(reason), - InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)); - } - onShowHideSoftInputRequested(false /* show */, windowToken, reason, statsToken); - } + mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason); } else { ImeTracker.get().onCancelled(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE); } @@ -4758,14 +4713,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); return; } + final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(windowToken); if (!setVisible) { if (mCurClient != null) { ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); - - mWindowManagerInternal.hideIme( - mHideRequestWindowMap.get(windowToken), - mCurClient.mSelfReportedDisplayId, statsToken); + mWindowManagerInternal.hideIme(requestToken, mCurClient.mSelfReportedDisplayId, + statsToken); } else { ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); @@ -4774,8 +4728,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); // Send to window manager to show IME after IME layout finishes. - mWindowManagerInternal.showImePostLayout(mShowRequestWindowMap.get(windowToken), - statsToken); + mWindowManagerInternal.showImePostLayout(requestToken, statsToken); } } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -4820,7 +4773,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub /** Called right after {@link com.android.internal.inputmethod.IInputMethod#showSoftInput}. */ @GuardedBy("ImfLock.class") - private void onShowHideSoftInputRequested(boolean show, IBinder requestToken, + void onShowHideSoftInputRequested(boolean show, IBinder requestToken, @SoftInputShowHideReason int reason, @Nullable ImeTracker.Token statsToken) { final WindowManagerInternal.ImeTargetInfo info = mWindowManagerInternal.onToggleImeRequested( From 351bf0fa35fd563ce2d99229a4cdc78a7d3ef76f Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Mon, 17 Oct 2022 01:13:38 +0800 Subject: [PATCH 3/6] Migrate to IME visibility settings Move following fields from IMMS to ImeVisibilityComputer.ImePolicy - mImeHiddenByDisplayPolicy - mAccessibilityRequestingNoKeyboard Bug: 246309664 Test: atest CtsInputMethodTestCases Change-Id: I2aea907b60f51829fbe8c1e8386dab77388e9694 --- .../ImeVisibilityStateComputer.java | 37 ++++++++++++++++--- .../InputMethodManagerService.java | 34 +++++------------ 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 2374995843002..8a90ae02b3571 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -16,6 +16,8 @@ package com.android.server.inputmethod; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; +import static android.server.inputmethod.InputMethodManagerServiceProto.ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD; import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_EXPLICITLY_REQUESTED; import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FORCED; import static android.view.Display.DEFAULT_DISPLAY; @@ -24,6 +26,7 @@ import static android.view.WindowManager.LayoutParams.SoftInputModeFlags; import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString; +import android.accessibilityservice.AccessibilityService; import android.annotation.IntDef; import android.annotation.NonNull; import android.os.IBinder; @@ -123,14 +126,21 @@ public final class ImeVisibilityStateComputer { * @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. + * @return {@code true} when the computer has proceed this show request operation. */ - void onImeShowFlags(int showFlags) { + boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, int showFlags) { + if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) { + ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY); + return false; + } + ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY); if ((showFlags & InputMethodManager.SHOW_FORCED) != 0) { mRequestedShowExplicitly = true; mShowForced = true; } else if ((showFlags & InputMethodManager.SHOW_IMPLICIT) == 0) { mRequestedShowExplicitly = true; } + return true; } /** @@ -229,12 +239,15 @@ public final class ImeVisibilityStateComputer { void dumpDebug(ProtoOutputStream proto, long fieldId) { proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly); proto.write(SHOW_FORCED, mShowForced); + proto.write(ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD, + mPolicy.isA11yRequestNoSoftKeyboard()); } void dump(PrintWriter pw) { final Printer p = new PrintWriterPrinter(pw); p.println(" mRequestedShowExplicitly=" + mRequestedShowExplicitly + " mShowForced=" + mShowForced); + p.println(" mImeHiddenByDisplayPolicy=" + mPolicy.isImeHiddenByDisplayPolicy()); } /** @@ -254,17 +267,31 @@ public final class ImeVisibilityStateComputer { private boolean mImeHiddenByDisplayPolicy; /** - * Set when a11y requests to hide IME by A11yService#setShowMode(SHOW_MODE_HIDDEN) + * Set when the accessibility service requests to hide IME by + * {@link AccessibilityService.SoftKeyboardController#setShowMode} */ - private boolean mAccessibilityRequestingNoSoftKeyboard; + private boolean mA11yRequestingNoSoftKeyboard; void setImeHiddenByDisplayPolicy(boolean hideIme) { mImeHiddenByDisplayPolicy = hideIme; } - void setA11yRequestNoSoftKeyboard(boolean a11yRequestNoIme) { - mAccessibilityRequestingNoSoftKeyboard = a11yRequestNoIme; + boolean isImeHiddenByDisplayPolicy() { + return mImeHiddenByDisplayPolicy; } + + void setA11yRequestNoSoftKeyboard(int keyboardShowMode) { + mA11yRequestingNoSoftKeyboard = + (keyboardShowMode & AccessibilityService.SHOW_MODE_MASK) == SHOW_MODE_HIDDEN; + } + + boolean isA11yRequestNoSoftKeyboard() { + return mA11yRequestingNoSoftKeyboard; + } + } + + ImeVisibilityPolicy getImePolicy() { + return mPolicy; } /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 5ca08b878f9d4..f26a9bb0f6328 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -20,7 +20,6 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.server.inputmethod.InputMethodManagerServiceProto.ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD; import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DISPOSITION; import static android.server.inputmethod.InputMethodManagerServiceProto.BOUND_TO_METHOD; import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ATTRIBUTE; @@ -54,7 +53,6 @@ import static com.android.server.inputmethod.InputMethodUtils.isSoftInputModeSta import static java.lang.annotation.RetentionPolicy.SOURCE; import android.Manifest; -import android.accessibilityservice.AccessibilityService; import android.annotation.AnyThread; import android.annotation.BinderThread; import android.annotation.DrawableRes; @@ -530,13 +528,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mBindingController.advanceSequenceNumber(); } - /** - * {@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. - */ - boolean mImeHiddenByDisplayPolicy; - /** * The client that is currently bound to an input method. */ @@ -777,7 +768,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub int mImeWindowVis; private LocaleList mLastSystemLocales; - private boolean mAccessibilityRequestingNoSoftKeyboard; private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); private final String mSlotIme; @@ -1182,11 +1172,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } else if (accessibilityRequestingNoImeUri.equals(uri)) { final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser( mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0 /* def */, mUserId); - mAccessibilityRequestingNoSoftKeyboard = - (accessibilitySoftKeyboardSetting & AccessibilityService.SHOW_MODE_MASK) - == AccessibilityService.SHOW_MODE_HIDDEN; - if (mAccessibilityRequestingNoSoftKeyboard) { + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId); + mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard( + accessibilitySoftKeyboardSetting); + if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) { final boolean showRequested = mShowRequested; hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, null /* resultReceiver */, @@ -2477,15 +2466,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // session & other conditions. mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.mSelfReportedDisplayId, mImeDisplayValidator); + final boolean imeHiddenByPolicy = mDisplayIdToShowIme == INVALID_DISPLAY; + mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(imeHiddenByPolicy); - if (mDisplayIdToShowIme == INVALID_DISPLAY) { - mImeHiddenByDisplayPolicy = true; + if (imeHiddenByPolicy) { hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, null /* resultReceiver */, SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE); return InputBindResult.NO_IME; } - mImeHiddenByDisplayPolicy = false; if (mCurClient != cs) { prepareClientSwitchLocked(cs); @@ -3383,13 +3372,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // TODO(b/246309664): make mShowRequested as per-window state. mShowRequested = true; - if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) { - ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY); + + if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) { return false; } - ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY); - mVisibilityStateComputer.onImeShowFlags(flags); if (!mSystemReady) { ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_SYSTEM_READY); return false; @@ -4678,8 +4665,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub proto.write(BACK_DISPOSITION, mBackDisposition); proto.write(IME_WINDOW_VISIBILITY, mImeWindowVis); proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard()); - proto.write(ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD, - mAccessibilityRequestingNoSoftKeyboard); proto.end(token); } } @@ -5909,7 +5894,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mInFullscreenMode=" + mInFullscreenMode); p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive); p.println(" mSettingsObserver=" + mSettingsObserver); - p.println(" mImeHiddenByDisplayPolicy=" + mImeHiddenByDisplayPolicy); p.println(" mStylusIds=" + (mStylusIds != null ? Arrays.toString(mStylusIds.toArray()) : "")); p.println(" mSwitchingController:"); From 537a99fc904a9aa5c242e5df804fb3fe4f85b68e Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Mon, 24 Oct 2022 17:18:11 +0800 Subject: [PATCH 4/6] Migrate computeImeDisplayIdForTarget to ImeVisibilityStateComputer To abtract the IME display computatation and this CL should not have any behavior change. Bug: 246309664 Test: atest CtsInputMethodTestCases InputMethodManagerServiceTests Change-Id: I9e15358a1b101a526828115245a85964f06da4bd --- .../inputmethod/ImeVisibilityStateComputer.java | 13 +++++++++++++ .../inputmethod/InputMethodManagerService.java | 16 ++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 8a90ae02b3571..e679f2655f64b 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -21,10 +21,12 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.ACCESSIB 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.Display.INVALID_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 static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget; import android.accessibilityservice.AccessibilityService; import android.annotation.IntDef; @@ -59,6 +61,8 @@ public final class ImeVisibilityStateComputer { private final InputMethodManagerService mService; private final WindowManagerInternal mWindowManagerInternal; + final InputMethodManagerService.ImeDisplayValidator mImeDisplayValidator; + /** * 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. @@ -118,6 +122,7 @@ public final class ImeVisibilityStateComputer { public ImeVisibilityStateComputer(InputMethodManagerService service) { mService = service; mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); + mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy; mPolicy = new ImeVisibilityPolicy(); } @@ -183,6 +188,14 @@ public final class ImeVisibilityStateComputer { mShowForced = false; } + int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) { + final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator); + state.setImeDisplayId(displayToShowIme); + final boolean imeHiddenByPolicy = displayToShowIme == INVALID_DISPLAY; + mPolicy.setImeHiddenByDisplayPolicy(imeHiddenByPolicy); + return displayToShowIme; + } + /** * Request to show/hide IME from the given window. * diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index f26a9bb0f6328..cdeeb18fc767e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -691,8 +691,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub */ private static final int FALLBACK_DISPLAY_ID = DEFAULT_DISPLAY; - final ImeDisplayValidator mImeDisplayValidator; - /** * If non-null, this is the input method service we are currently connected * to. @@ -1686,7 +1684,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mImePlatformCompatUtils = new ImePlatformCompatUtils(); mInputMethodDeviceConfigs = new InputMethodDeviceConfigs(); - mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); @@ -2464,12 +2461,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Compute the final shown display ID with validated cs.selfReportedDisplayId for this // session & other conditions. - mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.mSelfReportedDisplayId, - mImeDisplayValidator); - final boolean imeHiddenByPolicy = mDisplayIdToShowIme == INVALID_DISPLAY; - mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(imeHiddenByPolicy); + ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull( + mCurFocusedWindow); + if (winState == null) { + return InputBindResult.NOT_IME_TARGET_WINDOW; + } + final int csDisplayId = cs.mSelfReportedDisplayId; + mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId); - if (imeHiddenByPolicy) { + if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) { hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, null /* resultReceiver */, SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE); From 67742336f4410e7757e3a5aea1043a8f6432241c Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Mon, 24 Oct 2022 21:03:53 +0800 Subject: [PATCH 5/6] Migrate applyImeVsibility to ImeVisibilityApplier To abstract the application of IME visiblity without any behavior change. Bug: 246309664 Test: atest CtsInputMethodTestCases Change-Id: I6e137a6e14f4fd7101322f54fda32ddcb5ccd017 --- .../DefaultImeVisibilityApplier.java | 41 +++++++++++++++++++ .../inputmethod/ImeVisibilityApplier.java | 9 ++-- .../InputMethodManagerService.java | 19 ++------- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index c85f151cfece4..86a08579e38b0 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -20,6 +20,8 @@ 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.STATE_HIDE_IME; +import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME; import android.annotation.Nullable; import android.os.Binder; @@ -32,6 +34,8 @@ import android.view.inputmethod.ImeTracker; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.server.LocalServices; +import com.android.server.wm.WindowManagerInternal; import java.util.Objects; @@ -47,8 +51,12 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { private InputMethodManagerService mService; + private final WindowManagerInternal mWindowManagerInternal; + + DefaultImeVisibilityApplier(InputMethodManagerService service) { mService = service; + mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); } @GuardedBy("ImfLock.class") @@ -111,4 +119,37 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { } } } + + @GuardedBy("ImfLock.class") + @Override + public void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + @ImeVisibilityStateComputer.VisibilityState int state) { + switch (state) { + case STATE_SHOW_IME: + ImeTracker.get().onProgress(statsToken, + ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); + // Send to window manager to show IME after IME layout finishes. + mWindowManagerInternal.showImePostLayout(windowToken, statsToken); + break; + case STATE_HIDE_IME: + if (mService.mCurFocusedWindowClient != null) { + ImeTracker.get().onProgress(statsToken, + ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); + // IMMS only knows of focused window, not the actual IME target. + // e.g. it isn't aware of any window that has both + // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target. + // Send it to window manager to hide IME from IME target window. + // TODO(b/139861270): send to mCurClient.client once IMMS is aware of + // actual IME target. + mWindowManagerInternal.hideIme(windowToken, + mService.mCurFocusedWindowClient.mSelfReportedDisplayId, statsToken); + } else { + ImeTracker.get().onFailed(statsToken, + ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); + } + break; + default: + throw new IllegalArgumentException("Invalid IME visibility state: " + state); + } + } } diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java index f398864f3b07f..e97ec93b23c83 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -58,12 +58,11 @@ interface ImeVisibilityApplier { * according to the given visibility state. * * @param windowToken The token of a window for applying the IME visibility - * @param state The new IME visibility state for the applier to handle + * @param statsToken A token that tracks the progress of an IME request. + * @param state The new IME visibility state for the applier to handle */ - default void applyImeVisibility(IBinder windowToken, - @ImeVisibilityStateComputer.VisibilityState int state) { - // TODO: migrate IMMS#applyImeVisibility logic to here. - } + default void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + @ImeVisibilityStateComputer.VisibilityState int state) {} /** * Updates the IME Z-ordering relative to the given window. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index cdeeb18fc767e..06ba880f66b68 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4699,22 +4699,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return; } final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(windowToken); - if (!setVisible) { - if (mCurClient != null) { - ImeTracker.get().onProgress(statsToken, - ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); - mWindowManagerInternal.hideIme(requestToken, mCurClient.mSelfReportedDisplayId, - statsToken); - } else { - ImeTracker.get().onFailed(statsToken, - ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); - } - } else { - ImeTracker.get().onProgress(statsToken, - ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); - // Send to window manager to show IME after IME layout finishes. - mWindowManagerInternal.showImePostLayout(requestToken, statsToken); - } + mVisibilityApplier.applyImeVisibility(requestToken, statsToken, + setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME + : ImeVisibilityStateComputer.STATE_HIDE_IME); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } From 279f7a585344b1ae0746b92085398acced54bbe3 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Tue, 15 Nov 2022 01:11:30 +0800 Subject: [PATCH 6/6] Migrate IMMS#shouldRestoreImeVisibility to ImeVisibilityStateComputer Bug: 246309664 Test: atest CtsInputMethodTestCases Change-Id: I6adbf430be28393832d7f9557e90f145c1a9ccba --- .../ImeVisibilityStateComputer.java | 24 +++++++++++++++++++ .../InputMethodManagerService.java | 20 ++++------------ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index e679f2655f64b..a2655f4c0f4d1 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -22,6 +22,7 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_EXP import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FORCED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.SoftInputModeFlags; @@ -249,6 +250,29 @@ public final class ImeVisibilityStateComputer { return mService.mCurFocusedWindow; } + IBinder getWindowTokenFrom(ImeTargetWindowState windowState) { + for (IBinder windowToken : mRequestWindowStateMap.keySet()) { + final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); + if (state == windowState) { + return windowToken; + } + } + return null; + } + + boolean shouldRestoreImeVisibility(@NonNull ImeTargetWindowState state) { + final int softInputMode = state.getSoftInputModeState(); + switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: + return false; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: + if ((softInputMode & SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + return false; + } + } + return mWindowManagerInternal.shouldRestoreImeVisibility(getWindowTokenFrom(state)); + } + void dumpDebug(ProtoOutputStream proto, long fieldId) { proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly); proto.write(SHOW_FORCED, mShowForced); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 06ba880f66b68..2dbbb1085bb11 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3666,8 +3666,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // 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)); + final ImeTargetWindowState windowState = new ImeTargetWindowState( + softInputMode, !sameWindowFocused, isTextEditor); + mVisibilityStateComputer.setWindowState(windowToken, windowState); if (sameWindowFocused && isTextEditor) { if (DEBUG) { @@ -3718,7 +3719,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Because the app might leverage these flags to hide soft-keyboard with showing their own // UI for input. if (isTextEditor && editorInfo != null - && shouldRestoreImeVisibility(windowToken, softInputMode)) { + && mVisibilityStateComputer.shouldRestoreImeVisibility(windowState)) { if (DEBUG) Slog.v(TAG, "Will show input to restore visibility"); res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, editorInfo, startInputFlags, startInputReason, unverifiedTargetSdkVersion, @@ -3907,19 +3908,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return true; } - private boolean shouldRestoreImeVisibility(IBinder windowToken, - @SoftInputModeFlags int softInputMode) { - switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) { - case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: - return false; - case LayoutParams.SOFT_INPUT_STATE_HIDDEN: - if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - return false; - } - } - return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken); - } - @GuardedBy("ImfLock.class") private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { final int uid = Binder.getCallingUid();