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..86a08579e38b0 --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -0,0 +1,155 @@ +/* + * 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 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; +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 com.android.server.LocalServices; +import com.android.server.wm.WindowManagerInternal; + +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; + + private final WindowManagerInternal mWindowManagerInternal; + + + DefaultImeVisibilityApplier(InputMethodManagerService service) { + mService = service; + mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); + } + + @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); + } + } + } + + @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 new file mode 100644 index 0000000000000..e97ec93b23c83 --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -0,0 +1,79 @@ +/* + * 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 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, @Nullable ImeTracker.Token statsToken, + @ImeVisibilityStateComputer.VisibilityState int state) {} + + /** + * 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 new file mode 100644 index 0000000000000..a2655f4c0f4d1 --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -0,0 +1,424 @@ +/* + * 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.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; +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; + +import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString; +import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget; + +import android.accessibilityservice.AccessibilityService; +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; + + 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. + */ + 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); + mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy; + 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. + * @return {@code true} when the computer has proceed this show request operation. + */ + 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; + } + + /** + * 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; + } + + 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. + * + * @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 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); + 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; + } + } + // Fallback to the focused window for some edge cases (e.g. relaunching the activity) + 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); + 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()); + } + + /** + * 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 the accessibility service requests to hide IME by + * {@link AccessibilityService.SoftKeyboardController#setShowMode} + */ + private boolean mA11yRequestingNoSoftKeyboard; + + void setImeHiddenByDisplayPolicy(boolean hideIme) { + mImeHiddenByDisplayPolicy = hideIme; + } + + 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; + } + + /** + * 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..2dbbb1085bb11 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; @@ -39,8 +38,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; @@ -48,17 +45,14 @@ 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; 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; @@ -126,7 +120,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 +292,12 @@ 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; + + @GuardedBy("ImfLock.class") + @NonNull private final DefaultImeVisibilityApplier mVisibilityApplier; + /** * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}. * @@ -529,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. */ @@ -637,16 +629,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. */ @@ -709,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. @@ -786,7 +766,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; @@ -973,22 +952,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}. */ @@ -1207,11 +1170,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 */, @@ -1722,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); @@ -1747,6 +1708,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub ? bindingControllerForTesting : new InputMethodBindingController(this); mAutofillController = new AutofillSuggestionsController(this); + + mVisibilityStateComputer = new ImeVisibilityStateComputer(this); + mVisibilityApplier = new DefaultImeVisibilityApplier(this); + mPreventImeStartupUnlessTextEditor = mRes.getBoolean( com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor); mNonPreemptibleInputMethods = mRes.getStringArray( @@ -2339,29 +2304,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 +2345,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); } @@ -2518,17 +2461,20 @@ 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); + 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 (mDisplayIdToShowIme == INVALID_DISPLAY) { - mImeHiddenByDisplayPolicy = true; + if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) { 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); @@ -3385,6 +3331,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) { @@ -3419,18 +3370,11 @@ 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); - return false; - } - 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; + if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) { + return false; } if (!mSystemReady) { @@ -3439,39 +3383,25 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } 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(); + 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 = getImeShowFlagsLocked(); - 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; } @@ -3527,20 +3457,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,49 +3468,30 @@ 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); // 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); - } - res = true; + mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason); } 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 +3638,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 +3664,12 @@ 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). + final ImeTargetWindowState windowState = new ImeTargetWindowState( + softInputMode, !sameWindowFocused, isTextEditor); + mVisibilityStateComputer.setWindowState(windowToken, windowState); + if (sameWindowFocused && isTextEditor) { if (DEBUG) { Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client @@ -3812,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, @@ -4001,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(); @@ -4746,8 +4640,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())); @@ -4760,8 +4653,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); } } @@ -4795,25 +4686,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); return; } - if (!setVisible) { - if (mCurClient != null) { - ImeTracker.get().onProgress(statsToken, - ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); - - mWindowManagerInternal.hideIme( - mHideRequestWindowMap.get(windowToken), - 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(mShowRequestWindowMap.get(windowToken), - statsToken); - } + final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(windowToken); + mVisibilityApplier.applyImeVisibility(requestToken, statsToken, + setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME + : ImeVisibilityStateComputer.STATE_HIDE_IME); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -4857,7 +4733,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( @@ -5988,14 +5864,11 @@ 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); - p.println(" mImeHiddenByDisplayPolicy=" + mImeHiddenByDisplayPolicy); p.println(" mStylusIds=" + (mStylusIds != null ? Arrays.toString(mStylusIds.toArray()) : "")); p.println(" mSwitchingController:");