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