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