1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.inputmethod;
18 
19 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
20 import static android.server.inputmethod.InputMethodManagerServiceProto.ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD;
21 import static android.server.inputmethod.InputMethodManagerServiceProto.INPUT_SHOWN;
22 import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_EXPLICITLY_REQUESTED;
23 import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FORCED;
24 import static android.view.Display.DEFAULT_DISPLAY;
25 import static android.view.Display.INVALID_DISPLAY;
26 import static android.view.MotionEvent.TOOL_TYPE_UNKNOWN;
27 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
28 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
29 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
30 import static android.view.WindowManager.LayoutParams.SoftInputModeFlags;
31 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
32 
33 import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString;
34 import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
35 import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
36 import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget;
37 
38 import android.accessibilityservice.AccessibilityService;
39 import android.annotation.IntDef;
40 import android.annotation.NonNull;
41 import android.content.res.Configuration;
42 import android.os.Binder;
43 import android.os.IBinder;
44 import android.util.PrintWriterPrinter;
45 import android.util.Printer;
46 import android.util.Slog;
47 import android.util.proto.ProtoOutputStream;
48 import android.view.MotionEvent;
49 import android.view.WindowManager;
50 import android.view.inputmethod.Flags;
51 import android.view.inputmethod.ImeTracker;
52 import android.view.inputmethod.InputMethod;
53 import android.view.inputmethod.InputMethodManager;
54 
55 import com.android.internal.annotations.VisibleForTesting;
56 import com.android.internal.inputmethod.SoftInputShowHideReason;
57 import com.android.server.LocalServices;
58 import com.android.server.wm.ImeTargetChangeListener;
59 import com.android.server.wm.WindowManagerInternal;
60 
61 import java.io.PrintWriter;
62 import java.util.WeakHashMap;
63 
64 /**
65  * A computer used by {@link InputMethodManagerService} that computes the IME visibility state
66  * according the given {@link ImeTargetWindowState} from the focused window or the app requested IME
67  * visibility from {@link InputMethodManager}.
68  */
69 public final class ImeVisibilityStateComputer {
70 
71     private static final String TAG = "ImeVisibilityStateComputer";
72 
73     private static final boolean DEBUG = InputMethodManagerService.DEBUG;
74 
75     private final InputMethodManagerService mService;
76     private final WindowManagerInternal mWindowManagerInternal;
77 
78     final InputMethodManagerService.ImeDisplayValidator mImeDisplayValidator;
79 
80     /**
81      * A map used to track the requested IME target window and its state. The key represents the
82      * token of the window and the value is the corresponding IME window state.
83      */
84     private final WeakHashMap<IBinder, ImeTargetWindowState> mRequestWindowStateMap =
85             new WeakHashMap<>();
86 
87     /**
88      * Set if IME was explicitly told to show the input method.
89      *
90      * @see InputMethodManager#SHOW_IMPLICIT that we set the value is {@code false}.
91      * @see InputMethodManager#HIDE_IMPLICIT_ONLY that system will not hide IME when the value is
92      * {@code true}.
93      */
94     boolean mRequestedShowExplicitly;
95 
96     /**
97      * Set if we were forced to be shown.
98      *
99      * @see InputMethodManager#SHOW_FORCED
100      * @see InputMethodManager#HIDE_NOT_ALWAYS
101      */
102     boolean mShowForced;
103 
104     /**
105      * Set if we last told the input method to show itself.
106      */
107     private boolean mInputShown;
108 
109     /**
110      * Set if we called
111      * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}.
112      */
113     private boolean mRequestedImeScreenshot;
114 
115     /** The window token of the current visible IME layering target overlay. */
116     private IBinder mCurVisibleImeLayeringOverlay;
117 
118     /** The window token of the current visible IME input target. */
119     private IBinder mCurVisibleImeInputTarget;
120 
121     /** Represent the invalid IME visibility state */
122     public static final int STATE_INVALID = -1;
123 
124     /** State to handle hiding the IME window requested by the app. */
125     public static final int STATE_HIDE_IME = 0;
126 
127     /** State to handle showing the IME window requested by the app. */
128     public static final int STATE_SHOW_IME = 1;
129 
130     /** State to handle showing the IME window with making the overlay window above it.  */
131     public static final int STATE_SHOW_IME_ABOVE_OVERLAY = 2;
132 
133     /** State to handle showing the IME window with making the overlay window behind it.  */
134     public static final int STATE_SHOW_IME_BEHIND_OVERLAY = 3;
135 
136     /** State to handle showing an IME preview surface during the app was loosing the IME focus */
137     public static final int STATE_SHOW_IME_SNAPSHOT = 4;
138 
139     public static final int STATE_HIDE_IME_EXPLICIT = 5;
140 
141     public static final int STATE_HIDE_IME_NOT_ALWAYS = 6;
142 
143     public static final int STATE_SHOW_IME_IMPLICIT = 7;
144 
145     /** State to handle removing an IME preview surface when necessary. */
146     public static final int STATE_REMOVE_IME_SNAPSHOT = 8;
147 
148     @IntDef({
149             STATE_INVALID,
150             STATE_HIDE_IME,
151             STATE_SHOW_IME,
152             STATE_SHOW_IME_ABOVE_OVERLAY,
153             STATE_SHOW_IME_BEHIND_OVERLAY,
154             STATE_SHOW_IME_SNAPSHOT,
155             STATE_HIDE_IME_EXPLICIT,
156             STATE_HIDE_IME_NOT_ALWAYS,
157             STATE_SHOW_IME_IMPLICIT,
158             STATE_REMOVE_IME_SNAPSHOT,
159     })
160     @interface VisibilityState {}
161 
162     /**
163      * The policy to configure the IME visibility.
164      */
165     private final ImeVisibilityPolicy mPolicy;
166 
ImeVisibilityStateComputer(@onNull InputMethodManagerService service)167     public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service) {
168         this(service,
169                 LocalServices.getService(WindowManagerInternal.class),
170                 LocalServices.getService(WindowManagerInternal.class)::getDisplayImePolicy,
171                 new ImeVisibilityPolicy());
172     }
173 
174     @VisibleForTesting
ImeVisibilityStateComputer(@onNull InputMethodManagerService service, @NonNull Injector injector)175     public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
176             @NonNull Injector injector) {
177         this(service, injector.getWmService(), injector.getImeValidator(),
178                 new ImeVisibilityPolicy());
179     }
180 
181     interface Injector {
getWmService()182         default WindowManagerInternal getWmService() {
183             return null;
184         }
185 
getImeValidator()186         default InputMethodManagerService.ImeDisplayValidator getImeValidator() {
187             return null;
188         }
189     }
190 
ImeVisibilityStateComputer(InputMethodManagerService service, WindowManagerInternal wmService, InputMethodManagerService.ImeDisplayValidator imeDisplayValidator, ImeVisibilityPolicy imePolicy)191     private ImeVisibilityStateComputer(InputMethodManagerService service,
192             WindowManagerInternal wmService,
193             InputMethodManagerService.ImeDisplayValidator imeDisplayValidator,
194             ImeVisibilityPolicy imePolicy) {
195         mService = service;
196         mWindowManagerInternal = wmService;
197         mImeDisplayValidator = imeDisplayValidator;
198         mPolicy = imePolicy;
199         mWindowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() {
200             @Override
201             public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken,
202                     @WindowManager.LayoutParams.WindowType int windowType, boolean visible,
203                     boolean removed) {
204                 mCurVisibleImeLayeringOverlay =
205                         // Ignoring the starting window since it's ok to cover the IME target
206                         // window in temporary without affecting the IME visibility.
207                         (visible && !removed && windowType != TYPE_APPLICATION_STARTING)
208                                 ? overlayWindowToken : null;
209             }
210 
211             @Override
212             public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
213                     boolean visibleRequested, boolean removed) {
214                 if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed)
215                         && mCurVisibleImeLayeringOverlay != null) {
216                     final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
217                     final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
218                             ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */);
219                     mService.onApplyImeVisibilityFromComputer(imeInputTarget, statsToken,
220                             new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason));
221                 }
222                 mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null;
223             }
224         });
225     }
226 
227     /**
228      * Called when {@link InputMethodManagerService} is processing the show IME request.
229      *
230      * @param statsToken The token tracking the current IME request.
231      * @return {@code true} when the show request can proceed.
232      */
onImeShowFlags(@onNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int showFlags)233     boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken,
234             @InputMethodManager.ShowFlags int showFlags) {
235         if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) {
236             ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
237             return false;
238         }
239         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
240         // We only "set" the state corresponding to the flags, as this will be reset
241         // in clearImeShowFlags during a hide request.
242         // Thus, we keep the strongest values set (e.g. an implicit show right after
243         // an explicit show will still be considered explicit, likewise for forced).
244         if ((showFlags & InputMethodManager.SHOW_FORCED) != 0) {
245             mRequestedShowExplicitly = true;
246             mShowForced = true;
247         } else if ((showFlags & InputMethodManager.SHOW_IMPLICIT) == 0) {
248             mRequestedShowExplicitly = true;
249         }
250         return true;
251     }
252 
253     /**
254      * Called when {@link InputMethodManagerService} is processing the hide IME request.
255      *
256      * @param statsToken The token tracking the current IME request.
257      * @return {@code true} when the hide request can proceed.
258      */
canHideIme(@onNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int hideFlags)259     boolean canHideIme(@NonNull ImeTracker.Token statsToken,
260             @InputMethodManager.HideFlags int hideFlags) {
261         if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
262                 && (mRequestedShowExplicitly || mShowForced)) {
263             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
264             ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT);
265             return false;
266         }
267         if (mShowForced && (hideFlags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
268             if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
269             ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
270             return false;
271         }
272         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
273         return true;
274     }
275 
276     /**
277      * Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags}
278      * to {@link InputMethod.ShowFlags}.
279      */
280     @InputMethod.ShowFlags
getShowFlagsForInputMethodServiceOnly()281     int getShowFlagsForInputMethodServiceOnly() {
282         int flags = 0;
283         if (mShowForced) {
284             flags |= InputMethod.SHOW_FORCED | InputMethod.SHOW_EXPLICIT;
285         } else if (mRequestedShowExplicitly) {
286             flags |= InputMethod.SHOW_EXPLICIT;
287         }
288         return flags;
289     }
290 
291     /**
292      * Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags}
293      * to {@link InputMethodManager.ShowFlags}.
294      */
295     @InputMethodManager.ShowFlags
getShowFlags()296     int getShowFlags() {
297         int flags = 0;
298         if (mShowForced) {
299             flags |= InputMethodManager.SHOW_FORCED;
300         } else if (!mRequestedShowExplicitly) {
301             flags |= InputMethodManager.SHOW_IMPLICIT;
302         }
303         return flags;
304     }
305 
clearImeShowFlags()306     void clearImeShowFlags() {
307         mRequestedShowExplicitly = false;
308         mShowForced = false;
309         mInputShown = false;
310     }
311 
computeImeDisplayId(@onNull ImeTargetWindowState state, int displayId)312     int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) {
313         final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
314         state.setImeDisplayId(displayToShowIme);
315         final boolean imeHiddenByPolicy = displayToShowIme == INVALID_DISPLAY;
316         mPolicy.setImeHiddenByDisplayPolicy(imeHiddenByPolicy);
317         return displayToShowIme;
318     }
319 
320     /**
321      * Request to show/hide IME from the given window.
322      *
323      * @param windowToken The window which requests to show/hide IME.
324      * @param showIme {@code true} means to show IME, {@code false} otherwise.
325      *                            Note that in the computer will take this option to compute the
326      *                            visibility state, it could be {@link #STATE_SHOW_IME} or
327      *                            {@link #STATE_HIDE_IME}.
328      */
requestImeVisibility(IBinder windowToken, boolean showIme)329     void requestImeVisibility(IBinder windowToken, boolean showIme) {
330         ImeTargetWindowState state = getOrCreateWindowState(windowToken);
331         if (!mPolicy.mPendingA11yRequestingHideKeyboard) {
332             state.setRequestedImeVisible(showIme);
333         } else {
334             // As A11y requests no IME is just a temporary, so we don't change the requested IME
335             // visible in case the last visibility state goes wrong after leaving from the a11y
336             // policy.
337             mPolicy.mPendingA11yRequestingHideKeyboard = false;
338         }
339         // create a placeholder token for IMS so that IMS cannot inject windows into client app.
340         state.setRequestImeToken(new Binder());
341         setWindowStateInner(windowToken, state);
342     }
343 
getOrCreateWindowState(IBinder windowToken)344     ImeTargetWindowState getOrCreateWindowState(IBinder windowToken) {
345         ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
346         if (state == null) {
347             state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNSPECIFIED, 0, false, false, false);
348         }
349         return state;
350     }
351 
getWindowStateOrNull(IBinder windowToken)352     ImeTargetWindowState getWindowStateOrNull(IBinder windowToken) {
353         ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
354         return state;
355     }
356 
setWindowState(IBinder windowToken, @NonNull ImeTargetWindowState newState)357     void setWindowState(IBinder windowToken, @NonNull ImeTargetWindowState newState) {
358         final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
359         if (state != null && newState.hasEditorFocused()
360                 && newState.mToolType != MotionEvent.TOOL_TYPE_STYLUS) {
361             // Inherit the last requested IME visible state when the target window is still
362             // focused with an editor.
363             newState.setRequestedImeVisible(state.mRequestedImeVisible);
364         }
365         setWindowStateInner(windowToken, newState);
366     }
367 
setWindowStateInner(IBinder windowToken, @NonNull ImeTargetWindowState newState)368     private void setWindowStateInner(IBinder windowToken, @NonNull ImeTargetWindowState newState) {
369         if (DEBUG) Slog.d(TAG, "setWindowStateInner, windowToken=" + windowToken
370                 + ", state=" + newState);
371         mRequestWindowStateMap.put(windowToken, newState);
372     }
373 
374     static class ImeVisibilityResult {
375         private final @VisibilityState int mState;
376         private final @SoftInputShowHideReason int mReason;
377 
ImeVisibilityResult(@isibilityState int state, @SoftInputShowHideReason int reason)378         ImeVisibilityResult(@VisibilityState int state, @SoftInputShowHideReason int reason) {
379             mState = state;
380             mReason = reason;
381         }
382 
getState()383         @VisibilityState int getState() {
384             return mState;
385         }
386 
getReason()387         @SoftInputShowHideReason int getReason() {
388             return mReason;
389         }
390     }
391 
computeState(ImeTargetWindowState state, boolean allowVisible)392     ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible) {
393         // TODO: Output the request IME visibility state according to the requested window state
394         final int softInputVisibility = state.mSoftInputModeState & SOFT_INPUT_MASK_STATE;
395         // Should we auto-show the IME even if the caller has not
396         // specified what should be done with it?
397         // We only do this automatically if the window can resize
398         // to accommodate the IME (so what the user sees will give
399         // them good context without input information being obscured
400         // by the IME) or if running on a large screen where there
401         // is more room for the target window + IME.
402         final boolean doAutoShow =
403                 (state.mSoftInputModeState & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
404                         == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
405                         || mService.mRes.getConfiguration().isLayoutSizeAtLeast(
406                         Configuration.SCREENLAYOUT_SIZE_LARGE);
407         final boolean isForwardNavigation = (state.mSoftInputModeState
408                 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
409 
410         // We shows the IME when the system allows the IME focused target window to restore the
411         // IME visibility (e.g. switching to the app task when last time the IME is visible).
412         // Note that we don't restore IME visibility for some cases (e.g. when the soft input
413         // state is ALWAYS_HIDDEN or STATE_HIDDEN with forward navigation).
414         // Because the app might leverage these flags to hide soft-keyboard with showing their own
415         // UI for input.
416         if (state.hasEditorFocused() && shouldRestoreImeVisibility(state)) {
417             if (DEBUG) Slog.v(TAG, "Will show input to restore visibility");
418             // Inherit the last requested IME visible state when the target window is still
419             // focused with an editor.
420             state.setRequestedImeVisible(true);
421             setWindowStateInner(getWindowTokenFrom(state), state);
422             return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
423                     SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
424         }
425 
426         switch (softInputVisibility) {
427             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
428                 if (state.hasImeFocusChanged() && (!state.hasEditorFocused() || (!doAutoShow
429                         && !Flags.refactorInsetsController()))) {
430                     if (WindowManager.LayoutParams.mayUseInputMethod(state.getWindowFlags())) {
431                         // There is no focus view, and this window will
432                         // be behind any soft input window, so hide the
433                         // soft input window if it is shown.
434                         if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
435                         return new ImeVisibilityResult(STATE_HIDE_IME_NOT_ALWAYS,
436                                 SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
437                     }
438                 } else if (state.hasEditorFocused() && doAutoShow && isForwardNavigation) {
439                     // There is a focus view, and we are navigating forward
440                     // into the window, so show the input window for the user.
441                     // We only do this automatically if the window can resize
442                     // to accommodate the IME (so what the user sees will give
443                     // them good context without input information being obscured
444                     // by the IME) or if running on a large screen where there
445                     // is more room for the target window + IME.
446                     if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
447                     return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
448                             SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
449                 }
450                 break;
451             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
452                 // Do nothing but preserving the last IME requested visibility state.
453                 final ImeTargetWindowState lastState =
454                         getWindowStateOrNull(mService.mLastImeTargetWindow);
455                 if (lastState != null) {
456                     state.setRequestedImeVisible(lastState.mRequestedImeVisible);
457                 }
458                 break;
459             case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
460                 if (Flags.refactorInsetsController()) {
461                     // In this case, we don't have to manipulate the requested visible types of
462                     // the WindowState, as they're already in the correct state
463                     break;
464                 } else if (isForwardNavigation) {
465                     if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
466                     return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
467                             SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
468                 }
469                 break;
470             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
471                 if (Flags.refactorInsetsController()) {
472                     // In this case, we don't have to manipulate the requested visible types of
473                     // the WindowState, as they're already in the correct state
474                     break;
475                 } else if (state.hasImeFocusChanged()) {
476                     if (DEBUG) Slog.v(TAG, "Window asks to hide input");
477                     return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
478                             SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
479                 }
480                 break;
481             case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
482                 if (isForwardNavigation) {
483                     if (allowVisible) {
484                         if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
485                         return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
486                                 SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV);
487                     } else {
488                         Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
489                                 + " there is no focused view that also returns true from"
490                                 + " View#onCheckIsTextEditor()");
491                     }
492                 }
493                 break;
494             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
495                 if (DEBUG) Slog.v(TAG, "Window asks to always show input");
496                 if (allowVisible) {
497                     if (state.hasImeFocusChanged()) {
498                         return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
499                                 SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
500                     }
501                 } else {
502                     Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
503                             + " there is no focused view that also returns true from"
504                             + " View#onCheckIsTextEditor()");
505                 }
506                 break;
507         }
508 
509         if (!state.hasImeFocusChanged()) {
510             // On previous platforms, when Dialogs re-gained focus, the Activity behind
511             // would briefly gain focus first, and dismiss the IME.
512             // On R that behavior has been fixed, but unfortunately apps have come
513             // to rely on this behavior to hide the IME when the editor no longer has focus
514             // To maintain compatibility, we are now hiding the IME when we don't have
515             // an editor upon refocusing a window.
516             if (state.isStartInputByGainFocus()) {
517                 if (DEBUG) Slog.v(TAG, "Same window without editor will hide input");
518                 return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
519                         SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
520             }
521         }
522         if (!state.hasEditorFocused() && mInputShown && state.isStartInputByGainFocus()
523                 && mService.mInputMethodDeviceConfigs.shouldHideImeWhenNoEditorFocus()) {
524             // Hide the soft-keyboard when the system do nothing for softInputModeState
525             // of the window being gained focus without an editor. This behavior benefits
526             // to resolve some unexpected IME visible cases while that window with following
527             // configurations being switched from an IME shown window:
528             // 1) SOFT_INPUT_STATE_UNCHANGED state without an editor
529             // 2) SOFT_INPUT_STATE_VISIBLE state without an editor
530             // 3) SOFT_INPUT_STATE_ALWAYS_VISIBLE state without an editor
531             if (DEBUG) Slog.v(TAG, "Window without editor will hide input");
532             if (Flags.refactorInsetsController()) {
533                 state.setRequestedImeVisible(false);
534             }
535             return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
536                     SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR);
537         }
538         return null;
539     }
540 
541     @VisibleForTesting
onInteractiveChanged(IBinder windowToken, boolean interactive)542     ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) {
543         final ImeTargetWindowState state = getWindowStateOrNull(windowToken);
544         if (state != null && state.isRequestedImeVisible() && mInputShown && !interactive) {
545             mRequestedImeScreenshot = true;
546             return new ImeVisibilityResult(STATE_SHOW_IME_SNAPSHOT, SHOW_IME_SCREENSHOT_FROM_IMMS);
547         }
548         if (interactive && mRequestedImeScreenshot) {
549             mRequestedImeScreenshot = false;
550             return new ImeVisibilityResult(STATE_REMOVE_IME_SNAPSHOT,
551                     REMOVE_IME_SCREENSHOT_FROM_IMMS);
552         }
553         return null;
554     }
555 
getWindowTokenFrom(IBinder requestImeToken)556     IBinder getWindowTokenFrom(IBinder requestImeToken) {
557         for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
558             final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
559             if (state.getRequestImeToken() == requestImeToken) {
560                 return windowToken;
561             }
562         }
563         // Fallback to the focused window for some edge cases (e.g. relaunching the activity)
564         return mService.mImeBindingState.mFocusedWindow;
565     }
566 
getWindowTokenFrom(ImeTargetWindowState windowState)567     IBinder getWindowTokenFrom(ImeTargetWindowState windowState) {
568         for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
569             final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
570             if (state == windowState) {
571                 return windowToken;
572             }
573         }
574         return null;
575     }
576 
shouldRestoreImeVisibility(@onNull ImeTargetWindowState state)577     boolean shouldRestoreImeVisibility(@NonNull ImeTargetWindowState state) {
578         final int softInputMode = state.getSoftInputModeState();
579         switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
580             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
581                 return false;
582             case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
583                 if ((softInputMode & SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
584                     return false;
585                 }
586         }
587         return mWindowManagerInternal.shouldRestoreImeVisibility(getWindowTokenFrom(state));
588     }
589 
isInputShown()590     boolean isInputShown() {
591         return mInputShown;
592     }
593 
setInputShown(boolean inputShown)594     void setInputShown(boolean inputShown) {
595         mInputShown = inputShown;
596     }
597 
dumpDebug(ProtoOutputStream proto, long fieldId)598     void dumpDebug(ProtoOutputStream proto, long fieldId) {
599         proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly);
600         proto.write(SHOW_FORCED, mShowForced);
601         proto.write(ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD,
602                 mPolicy.isA11yRequestNoSoftKeyboard());
603         proto.write(INPUT_SHOWN, mInputShown);
604     }
605 
dump(@onNull PrintWriter pw, @NonNull String prefix)606     void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
607         final Printer p = new PrintWriterPrinter(pw);
608         p.println(prefix + "mRequestedShowExplicitly=" + mRequestedShowExplicitly
609                 + " mShowForced=" + mShowForced);
610         p.println(prefix + "mImeHiddenByDisplayPolicy=" + mPolicy.isImeHiddenByDisplayPolicy());
611         p.println(prefix + "mInputShown=" + mInputShown);
612     }
613 
614     /**
615      * A settings class to manage all IME related visibility policies or settings.
616      *
617      * This is used for the visibility computer to manage and tell
618      * {@link InputMethodManagerService} if the requested IME visibility is valid from
619      * application call or the focus window.
620      */
621     static class ImeVisibilityPolicy {
622         /**
623          * {@code true} if the Ime policy has been set to
624          * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}.
625          *
626          * This prevents the IME from showing when it otherwise may have shown.
627          */
628         private boolean mImeHiddenByDisplayPolicy;
629 
630         /**
631          * Set when the accessibility service requests to hide IME by
632          * {@link AccessibilityService.SoftKeyboardController#setShowMode}
633          */
634         private boolean mA11yRequestingNoSoftKeyboard;
635 
636         /**
637          * Used when A11y request to hide IME temporary when receiving
638          * {@link AccessibilityService#SHOW_MODE_HIDDEN} from
639          * {@link android.provider.Settings.Secure#ACCESSIBILITY_SOFT_KEYBOARD_MODE} without
640          * changing the requested IME visible state.
641          */
642         private boolean mPendingA11yRequestingHideKeyboard;
643 
setImeHiddenByDisplayPolicy(boolean hideIme)644         void setImeHiddenByDisplayPolicy(boolean hideIme) {
645             mImeHiddenByDisplayPolicy = hideIme;
646         }
647 
isImeHiddenByDisplayPolicy()648         boolean isImeHiddenByDisplayPolicy() {
649             return mImeHiddenByDisplayPolicy;
650         }
651 
setA11yRequestNoSoftKeyboard(int keyboardShowMode)652         void setA11yRequestNoSoftKeyboard(int keyboardShowMode) {
653             mA11yRequestingNoSoftKeyboard =
654                     (keyboardShowMode & AccessibilityService.SHOW_MODE_MASK) == SHOW_MODE_HIDDEN;
655             if (mA11yRequestingNoSoftKeyboard) {
656                 mPendingA11yRequestingHideKeyboard = true;
657             }
658         }
659 
isA11yRequestNoSoftKeyboard()660         boolean isA11yRequestNoSoftKeyboard() {
661             return mA11yRequestingNoSoftKeyboard;
662         }
663     }
664 
getImePolicy()665     ImeVisibilityPolicy getImePolicy() {
666         return mPolicy;
667     }
668 
669     /**
670      * A class that represents the current state of the IME target window.
671      */
672     static class ImeTargetWindowState {
673 
ImeTargetWindowState(@oftInputModeFlags int softInputModeState, int windowFlags, boolean imeFocusChanged, boolean hasFocusedEditor, boolean isStartInputByGainFocus)674         ImeTargetWindowState(@SoftInputModeFlags int softInputModeState, int windowFlags,
675                 boolean imeFocusChanged, boolean hasFocusedEditor,
676                 boolean isStartInputByGainFocus) {
677             this(softInputModeState, windowFlags, imeFocusChanged, hasFocusedEditor,
678                     isStartInputByGainFocus, TOOL_TYPE_UNKNOWN);
679         }
680 
ImeTargetWindowState(@oftInputModeFlags int softInputModeState, int windowFlags, boolean imeFocusChanged, boolean hasFocusedEditor, boolean isStartInputByGainFocus, @MotionEvent.ToolType int toolType)681         ImeTargetWindowState(@SoftInputModeFlags int softInputModeState, int windowFlags,
682                 boolean imeFocusChanged, boolean hasFocusedEditor,
683                 boolean isStartInputByGainFocus, @MotionEvent.ToolType int toolType) {
684             mSoftInputModeState = softInputModeState;
685             mWindowFlags = windowFlags;
686             mImeFocusChanged = imeFocusChanged;
687             mHasFocusedEditor = hasFocusedEditor;
688             mIsStartInputByGainFocus = isStartInputByGainFocus;
689             mToolType = toolType;
690         }
691 
692         /**
693          * Visibility state for this window. By default no state has been specified.
694          */
695         private final @SoftInputModeFlags int mSoftInputModeState;
696 
697         private final int mWindowFlags;
698 
699         /**
700          * {@link MotionEvent#getToolType(int)} that was used to click editor.
701          */
702         private final int mToolType;
703 
704         /**
705          * {@code true} means the IME focus changed from the previous window, {@code false}
706          * otherwise.
707          */
708         private final boolean mImeFocusChanged;
709 
710         /**
711          * {@code true} when the window has focused an editor, {@code false} otherwise.
712          */
713         private final boolean mHasFocusedEditor;
714 
715         private final boolean mIsStartInputByGainFocus;
716 
717         /**
718          * Set if the client has asked for the input method to be shown.
719          */
720         private boolean mRequestedImeVisible;
721 
722         /**
723          * A identifier for knowing the requester of {@link InputMethodManager#showSoftInput} or
724          * {@link InputMethodManager#hideSoftInputFromWindow}.
725          */
726         private IBinder mRequestImeToken;
727 
728         /**
729          * The IME target display id for which the latest startInput was called.
730          */
731         private int mImeDisplayId = DEFAULT_DISPLAY;
732 
hasImeFocusChanged()733         boolean hasImeFocusChanged() {
734             return mImeFocusChanged;
735         }
736 
hasEditorFocused()737         boolean hasEditorFocused() {
738             return mHasFocusedEditor;
739         }
740 
isStartInputByGainFocus()741         boolean isStartInputByGainFocus() {
742             return mIsStartInputByGainFocus;
743         }
744 
getSoftInputModeState()745         int getSoftInputModeState() {
746             return mSoftInputModeState;
747         }
748 
getWindowFlags()749         int getWindowFlags() {
750             return mWindowFlags;
751         }
752 
getToolType()753         int getToolType() {
754             return mToolType;
755         }
756 
setImeDisplayId(int imeDisplayId)757         private void setImeDisplayId(int imeDisplayId) {
758             mImeDisplayId = imeDisplayId;
759         }
760 
getImeDisplayId()761         int getImeDisplayId() {
762             return mImeDisplayId;
763         }
764 
setRequestedImeVisible(boolean requestedImeVisible)765         private void setRequestedImeVisible(boolean requestedImeVisible) {
766             mRequestedImeVisible = requestedImeVisible;
767         }
768 
isRequestedImeVisible()769         boolean isRequestedImeVisible() {
770             return mRequestedImeVisible;
771         }
772 
setRequestImeToken(IBinder token)773         void setRequestImeToken(IBinder token) {
774             mRequestImeToken = token;
775         }
776 
getRequestImeToken()777         IBinder getRequestImeToken() {
778             return mRequestImeToken;
779         }
780 
781         @Override
toString()782         public String toString() {
783             return "ImeTargetWindowState{ imeToken " + mRequestImeToken
784                     + " imeFocusChanged " + mImeFocusChanged
785                     + " hasEditorFocused " + mHasFocusedEditor
786                     + " requestedImeVisible " + mRequestedImeVisible
787                     + " imeDisplayId " + mImeDisplayId
788                     + " softInputModeState " + softInputModeToString(mSoftInputModeState)
789                     + " isStartInputByGainFocus " + mIsStartInputByGainFocus
790                     + "}";
791         }
792     }
793 }
794