1 /* 2 * Copyright (C) 2007-2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.view.inputmethod; 18 19 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_IMMEDIATE; 21 import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_MONITOR; 22 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID; 23 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO; 24 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER; 25 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION; 26 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION_CALL; 27 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER; 28 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL; 29 import static android.view.inputmethod.InputMethodManagerProto.ACTIVE; 30 import static android.view.inputmethod.InputMethodManagerProto.CUR_ID; 31 import static android.view.inputmethod.InputMethodManagerProto.FULLSCREEN_MODE; 32 import static android.view.inputmethod.InputMethodManagerProto.NEXT_SERVED_VIEW; 33 import static android.view.inputmethod.InputMethodManagerProto.SERVED_CONNECTING; 34 import static android.view.inputmethod.InputMethodManagerProto.SERVED_VIEW; 35 36 import static com.android.internal.inputmethod.StartInputReason.BOUND_TO_IMMS; 37 38 import android.Manifest; 39 import android.annotation.DisplayContext; 40 import android.annotation.DrawableRes; 41 import android.annotation.DurationMillisLong; 42 import android.annotation.IntDef; 43 import android.annotation.NonNull; 44 import android.annotation.Nullable; 45 import android.annotation.RequiresFeature; 46 import android.annotation.RequiresPermission; 47 import android.annotation.SuppressLint; 48 import android.annotation.SystemApi; 49 import android.annotation.SystemService; 50 import android.annotation.TestApi; 51 import android.annotation.UiThread; 52 import android.annotation.UserIdInt; 53 import android.app.ActivityThread; 54 import android.app.PropertyInvalidatedCache; 55 import android.compat.annotation.ChangeId; 56 import android.compat.annotation.EnabledSince; 57 import android.compat.annotation.UnsupportedAppUsage; 58 import android.content.ComponentName; 59 import android.content.ContentResolver; 60 import android.content.Context; 61 import android.content.Intent; 62 import android.content.pm.PackageManager; 63 import android.graphics.Matrix; 64 import android.graphics.Rect; 65 import android.hardware.display.DisplayManager; 66 import android.inputmethodservice.InputMethodService; 67 import android.os.Binder; 68 import android.os.Build; 69 import android.os.Bundle; 70 import android.os.Handler; 71 import android.os.IBinder; 72 import android.os.Looper; 73 import android.os.Message; 74 import android.os.Process; 75 import android.os.ResultReceiver; 76 import android.os.SystemProperties; 77 import android.os.Trace; 78 import android.os.UserHandle; 79 import android.provider.Settings; 80 import android.text.TextUtils; 81 import android.text.style.SuggestionSpan; 82 import android.util.Log; 83 import android.util.Pair; 84 import android.util.Pools.Pool; 85 import android.util.Pools.SimplePool; 86 import android.util.PrintWriterPrinter; 87 import android.util.Printer; 88 import android.util.SparseArray; 89 import android.util.proto.ProtoOutputStream; 90 import android.view.Display; 91 import android.view.ImeFocusController; 92 import android.view.ImeInsetsSourceConsumer; 93 import android.view.InputChannel; 94 import android.view.InputEvent; 95 import android.view.InputEventSender; 96 import android.view.KeyEvent; 97 import android.view.View; 98 import android.view.ViewRootImpl; 99 import android.view.WindowInsets; 100 import android.view.WindowManager; 101 import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 102 import android.view.autofill.AutofillId; 103 import android.view.autofill.AutofillManager; 104 import android.widget.Editor; 105 import android.window.ImeOnBackInvokedDispatcher; 106 import android.window.WindowOnBackInvokedDispatcher; 107 108 import com.android.internal.annotations.GuardedBy; 109 import com.android.internal.inputmethod.DirectBootAwareness; 110 import com.android.internal.inputmethod.IInputMethodClient; 111 import com.android.internal.inputmethod.IInputMethodSession; 112 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; 113 import com.android.internal.inputmethod.ImeTracing; 114 import com.android.internal.inputmethod.InputBindResult; 115 import com.android.internal.inputmethod.InputMethodDebug; 116 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry; 117 import com.android.internal.inputmethod.SoftInputShowHideReason; 118 import com.android.internal.inputmethod.StartInputFlags; 119 import com.android.internal.inputmethod.StartInputReason; 120 import com.android.internal.inputmethod.UnbindReason; 121 import com.android.internal.os.SomeArgs; 122 import com.android.internal.view.IInputMethodManager; 123 124 import java.io.FileDescriptor; 125 import java.io.PrintWriter; 126 import java.lang.annotation.Retention; 127 import java.lang.annotation.RetentionPolicy; 128 import java.lang.reflect.Proxy; 129 import java.util.Arrays; 130 import java.util.Collections; 131 import java.util.Comparator; 132 import java.util.List; 133 import java.util.Map; 134 import java.util.Objects; 135 import java.util.concurrent.CountDownLatch; 136 import java.util.concurrent.TimeUnit; 137 import java.util.concurrent.atomic.AtomicBoolean; 138 import java.util.function.Consumer; 139 140 /** 141 * Central system API to the overall input method framework (IMF) architecture, 142 * which arbitrates interaction between applications and the current input method. 143 * 144 * <p>Topics covered here: 145 * <ol> 146 * <li><a href="#ArchitectureOverview">Architecture Overview</a> 147 * <li><a href="#Applications">Applications</a> 148 * <li><a href="#InputMethods">Input Methods</a> 149 * <li><a href="#Security">Security</a> 150 * </ol> 151 * 152 * <a name="ArchitectureOverview"></a> 153 * <h3>Architecture Overview</h3> 154 * 155 * <p>There are three primary parties involved in the input method 156 * framework (IMF) architecture:</p> 157 * 158 * <ul> 159 * <li> The <strong>input method manager</strong> as expressed by this class 160 * is the central point of the system that manages interaction between all 161 * other parts. It is expressed as the client-side API here which exists 162 * in each application context and communicates with a global system service 163 * that manages the interaction across all processes. 164 * <li> An <strong>input method (IME)</strong> implements a particular 165 * interaction model allowing the user to generate text. The system binds 166 * to the current input method that is in use, causing it to be created and run, 167 * and tells it when to hide and show its UI. Only one IME is running at a time. 168 * <li> Multiple <strong>client applications</strong> arbitrate with the input 169 * method manager for input focus and control over the state of the IME. Only 170 * one such client is ever active (working with the IME) at a time. 171 * </ul> 172 * 173 * 174 * <a name="Applications"></a> 175 * <h3>Applications</h3> 176 * 177 * <p>In most cases, applications that are using the standard 178 * {@link android.widget.TextView} or its subclasses will have little they need 179 * to do to work well with soft input methods. The main things you need to 180 * be aware of are:</p> 181 * 182 * <ul> 183 * <li> Properly set the {@link android.R.attr#inputType} in your editable 184 * text views, so that the input method will have enough context to help the 185 * user in entering text into them. 186 * <li> Deal well with losing screen space when the input method is 187 * displayed. Ideally an application should handle its window being resized 188 * smaller, but it can rely on the system performing panning of the window 189 * if needed. You should set the {@link android.R.attr#windowSoftInputMode} 190 * attribute on your activity or the corresponding values on windows you 191 * create to help the system determine whether to pan or resize (it will 192 * try to determine this automatically but may get it wrong). 193 * <li> You can also control the preferred soft input state (open, closed, etc) 194 * for your window using the same {@link android.R.attr#windowSoftInputMode} 195 * attribute. 196 * </ul> 197 * 198 * <p>More finer-grained control is available through the APIs here to directly 199 * interact with the IMF and its IME -- either showing or hiding the input 200 * area, letting the user pick an input method, etc.</p> 201 * 202 * <p>For the rare people amongst us writing their own text editors, you 203 * will need to implement {@link android.view.View#onCreateInputConnection} 204 * to return a new instance of your own {@link InputConnection} interface 205 * allowing the IME to interact with your editor.</p> 206 * 207 * 208 * <a name="InputMethods"></a> 209 * <h3>Input Methods</h3> 210 * 211 * <p>An input method (IME) is implemented 212 * as a {@link android.app.Service}, typically deriving from 213 * {@link android.inputmethodservice.InputMethodService}. It must provide 214 * the core {@link InputMethod} interface, though this is normally handled by 215 * {@link android.inputmethodservice.InputMethodService} and implementors will 216 * only need to deal with the higher-level API there.</p> 217 * 218 * See the {@link android.inputmethodservice.InputMethodService} class for 219 * more information on implementing IMEs. 220 * 221 * 222 * <a name="Security"></a> 223 * <h3>Security</h3> 224 * 225 * <p>There are a lot of security issues associated with input methods, 226 * since they essentially have freedom to completely drive the UI and monitor 227 * everything the user enters. The Android input method framework also allows 228 * arbitrary third party IMEs, so care must be taken to restrict their 229 * selection and interactions.</p> 230 * 231 * <p>Here are some key points about the security architecture behind the 232 * IMF:</p> 233 * 234 * <ul> 235 * <li> <p>Only the system is allowed to directly access an IME's 236 * {@link InputMethod} interface, via the 237 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission. This is 238 * enforced in the system by not binding to an input method service that does 239 * not require this permission, so the system can guarantee no other untrusted 240 * clients are accessing the current input method outside of its control.</p> 241 * 242 * <li> <p>There may be many client processes of the IMF, but only one may 243 * be active at a time. The inactive clients can not interact with key 244 * parts of the IMF through the mechanisms described below.</p> 245 * 246 * <li> <p>Clients of an input method are only given access to its 247 * {@link InputMethodSession} interface. One instance of this interface is 248 * created for each client, and only calls from the session associated with 249 * the active client will be processed by the current IME. This is enforced 250 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal 251 * IMEs, but must be explicitly handled by an IME that is customizing the 252 * raw {@link InputMethodSession} implementation.</p> 253 * 254 * <li> <p>Only the active client's {@link InputConnection} will accept 255 * operations. The IMF tells each client process whether it is active, and 256 * the framework enforces that in inactive processes calls on to the current 257 * InputConnection will be ignored. This ensures that the current IME can 258 * only deliver events and text edits to the UI that the user sees as 259 * being in focus.</p> 260 * 261 * <li> <p>An IME can never interact with an {@link InputConnection} while 262 * the screen is off. This is enforced by making all clients inactive while 263 * the screen is off, and prevents bad IMEs from driving the UI when the user 264 * can not be aware of its behavior.</p> 265 * 266 * <li> <p>A client application can ask that the system let the user pick a 267 * new IME, but can not programmatically switch to one itself. This avoids 268 * malicious applications from switching the user to their own IME, which 269 * remains running when the user navigates away to another application. An 270 * IME, on the other hand, <em>is</em> allowed to programmatically switch 271 * the system to another IME, since it already has full control of user 272 * input.</p> 273 * 274 * <li> <p>The user must explicitly enable a new IME in settings before 275 * they can switch to it, to confirm with the system that they know about it 276 * and want to make it available for use.</p> 277 * </ul> 278 * 279 * <p>If your app targets Android 11 (API level 30) or higher, the methods in 280 * this class each return a filtered result by the rules of 281 * <a href="/training/basics/intents/package-visibility">package visibility</a>, 282 * except for the currently connected IME. Apps having a query for the 283 * {@link InputMethod#SERVICE_INTERFACE} see all IMEs.</p> 284 */ 285 @SystemService(Context.INPUT_METHOD_SERVICE) 286 @RequiresFeature(PackageManager.FEATURE_INPUT_METHODS) 287 public final class InputMethodManager { 288 private static final boolean DEBUG = false; 289 private static final String TAG = "InputMethodManager"; 290 291 private static final String PENDING_EVENT_COUNTER = "aq:imm"; 292 293 private static final int NOT_A_SUBTYPE_ID = -1; 294 295 /** 296 * A constant that represents Voice IME. 297 * 298 * @see InputMethodSubtype#getMode() 299 */ 300 private static final String SUBTYPE_MODE_VOICE = "voice"; 301 302 /** 303 * Provide this to {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocus(int, 304 * IInputMethodClient, IBinder, int, int, int, EditorInfo, 305 * com.android.internal.inputmethod.IRemoteInputConnection, IRemoteAccessibilityInputConnection, 306 * int, int, ImeOnBackInvokedDispatcher)} to receive 307 * {@link android.window.OnBackInvokedCallback} registrations from IME. 308 */ 309 private final ImeOnBackInvokedDispatcher mImeDispatcher = 310 new ImeOnBackInvokedDispatcher(Handler.getMain()) { 311 @Override 312 public WindowOnBackInvokedDispatcher getReceivingDispatcher() { 313 synchronized (mH) { 314 return mCurRootView != null ? mCurRootView.getOnBackInvokedDispatcher() : null; 315 } 316 } 317 }; 318 319 /** 320 * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly 321 * or indirectly relied on {@link #sInstance} via reflection or something like that. 322 * 323 * <p>Here are scenarios we know and there could be more scenarios we are not 324 * aware of right know.</p> 325 * 326 * <ul> 327 * <li>Apps that directly access {@link #sInstance} via reflection, which is currently 328 * allowed because of {@link UnsupportedAppUsage} annotation. Currently 329 * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that 330 * {@link #sInstance} is not {@code null} when such an app is accessing it, but removing 331 * that code from {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal 332 * untested code paths in their apps, which probably happen in an early startup time of that 333 * app.</li> 334 * <li>Apps that directly access {@link #peekInstance()} via reflection, which is currently 335 * allowed because of {@link UnsupportedAppUsage} annotation. Currently 336 * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that 337 * {@link #peekInstance()} returns non-{@code null} object when such an app is calling 338 * {@link #peekInstance()}, but removing that code from 339 * {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal untested code 340 * paths in their apps, which probably happen in an early startup time of that app. The good 341 * news is that unlike {@link #sInstance}'s case we can at least work around this scenario 342 * by changing the semantics of {@link #peekInstance()}, which is currently defined as 343 * "retrieve the global {@link InputMethodManager} instance, if it exists" to something that 344 * always returns non-{@code null} {@link InputMethodManager}. However, introducing such an 345 * workaround can also trigger different compatibility issues if {@link #peekInstance()} was 346 * called before {@link android.view.WindowManagerGlobal#getWindowSession()} and it expected 347 * {@link #peekInstance()} to return {@code null} as written in the JavaDoc.</li> 348 * </ul> 349 * 350 * <p>Since this is purely a compatibility hack, this method must be used only from 351 * {@link android.view.WindowManagerGlobal#getWindowSession()} and {@link #getInstance()}.</p> 352 * 353 * <p>TODO(Bug 116157766): Remove this method once we clean up {@link UnsupportedAppUsage}.</p> 354 * @hide 355 */ ensureDefaultInstanceForDefaultDisplayIfNecessary()356 public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { 357 forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper()); 358 } 359 360 private static final Object sLock = new Object(); 361 362 /** 363 * @deprecated This cannot be compatible with multi-display. Please do not use this. 364 */ 365 @Deprecated 366 @GuardedBy("sLock") 367 @UnsupportedAppUsage 368 static InputMethodManager sInstance; 369 370 /** 371 * Global map between display to {@link InputMethodManager}. 372 * 373 * <p>Currently this map works like a so-called leaky singleton. Once an instance is registered 374 * for the associated display ID, that instance will never be garbage collected.</p> 375 * 376 * <p>TODO(Bug 116699479): Implement instance clean up mechanism.</p> 377 */ 378 @GuardedBy("sLock") 379 private static final SparseArray<InputMethodManager> sInstanceMap = new SparseArray<>(); 380 381 /** 382 * Timeout in milliseconds for delivering a key to an IME. 383 */ 384 private static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500; 385 386 /** @hide */ 387 public static final int DISPATCH_IN_PROGRESS = -1; 388 389 /** @hide */ 390 public static final int DISPATCH_NOT_HANDLED = 0; 391 392 /** @hide */ 393 public static final int DISPATCH_HANDLED = 1; 394 395 /** @hide */ 396 public static final int SHOW_IM_PICKER_MODE_AUTO = 0; 397 /** @hide */ 398 public static final int SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES = 1; 399 /** @hide */ 400 public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2; 401 402 /** 403 * Clear {@link #SHOW_FORCED} flag when the next IME focused application changed. 404 * 405 * <p> 406 * Note that when this flag enabled in server side, {@link #SHOW_FORCED} will no longer 407 * affect the next focused application to keep showing IME, in case of unexpected IME visible 408 * when the next focused app isn't be the IME requester. </p> 409 * 410 * @hide 411 */ 412 @TestApi 413 @ChangeId 414 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 415 public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id. 416 417 /** 418 * If {@code true}, avoid calling the 419 * {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService} 420 * by skipping the call to {@link IInputMethodManager#startInputOrWindowGainedFocus} 421 * when we are switching focus between two non-editable views. This saves the cost of a binder 422 * call into the system server. 423 * <p><b>Note:</b> 424 * The default value is {@code true}. 425 */ 426 private static final boolean OPTIMIZE_NONEDITABLE_VIEWS = 427 SystemProperties.getBoolean("debug.imm.optimize_noneditable_views", true); 428 429 /** 430 * @deprecated Use {@link IInputMethodManagerGlobalInvoker} instead. 431 */ 432 @Deprecated 433 @UnsupportedAppUsage 434 final IInputMethodManager mService; 435 private final Looper mMainLooper; 436 437 // For scheduling work on the main thread. This also serves as our 438 // global lock. 439 // Remark on @UnsupportedAppUsage: there were context leaks on old versions 440 // of android (b/37043700), so developers used this field to perform manual clean up. 441 // Leaks were fixed, hacks were backported to AppCompatActivity, 442 // so an access to the field is closed. 443 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 444 final H mH; 445 446 // Our generic input connection if the current target does not have its own. 447 private final RemoteInputConnectionImpl mFallbackInputConnection; 448 449 private final int mDisplayId; 450 451 /** 452 * True if this input method client is active, initially false. 453 */ 454 @GuardedBy("mH") 455 private boolean mActive = false; 456 457 /** 458 * {@code true} if next {@link ImeFocusController#onPostWindowFocus} needs to 459 * restart input. 460 */ 461 @GuardedBy("mH") 462 private boolean mRestartOnNextWindowFocus = true; 463 464 /** 465 * As reported by IME through InputConnection. 466 */ 467 @GuardedBy("mH") 468 private boolean mFullscreenMode; 469 470 // ----------------------------------------------------------- 471 472 /** 473 * This is the view that should currently be served by an input method, 474 * regardless of the state of setting that up. 475 */ 476 @Nullable 477 @GuardedBy("mH") 478 private View mServedView; 479 480 /** 481 * This is the next view that will be served by the input method, when 482 * we get around to updating things. 483 */ 484 @Nullable 485 @GuardedBy("mH") 486 private View mNextServedView; 487 488 /** 489 * The latest {@link ViewRootImpl} that has, or most recently had, input method focus. 490 * 491 * <p>This value will be cleared when it becomes inactive and no longer has window focus. 492 */ 493 @Nullable 494 @GuardedBy("mH") 495 ViewRootImpl mCurRootView; 496 497 /** 498 * Whether the {@link #mCurRootView} currently has window focus. 499 */ 500 @GuardedBy("mH") 501 boolean mCurRootViewWindowFocused; 502 503 /** 504 * This is set when we are in the process of connecting, to determine 505 * when we have actually finished. 506 */ 507 @GuardedBy("mH") 508 private boolean mServedConnecting; 509 510 /** 511 * This is non-null when we have connected the served view; it holds 512 * the attributes that were last retrieved from the served view and given 513 * to the input connection. 514 */ 515 @GuardedBy("mH") 516 private EditorInfo mCurrentEditorInfo; 517 518 @GuardedBy("mH") 519 @Nullable 520 private ViewFocusParameterInfo mPreviousViewFocusParameters; 521 522 /** 523 * The InputConnection that was last retrieved from the served view. 524 */ 525 @GuardedBy("mH") 526 private RemoteInputConnectionImpl mServedInputConnection; 527 528 /** 529 * The completions that were last provided by the served view. 530 */ 531 @GuardedBy("mH") 532 private CompletionInfo[] mCompletions; 533 534 // Cursor position on the screen. 535 @GuardedBy("mH") 536 @UnsupportedAppUsage 537 Rect mTmpCursorRect = new Rect(); 538 539 @GuardedBy("mH") 540 @UnsupportedAppUsage 541 Rect mCursorRect = new Rect(); 542 543 /** Cached value for {@link #isStylusHandwritingAvailable} for userId. */ 544 @GuardedBy("mH") 545 private PropertyInvalidatedCache<Integer, Boolean> mStylusHandwritingAvailableCache; 546 547 private static final String CACHE_KEY_STYLUS_HANDWRITING_PROPERTY = 548 "cache_key.system_server.stylus_handwriting"; 549 550 @GuardedBy("mH") 551 private int mCursorSelStart; 552 @GuardedBy("mH") 553 private int mCursorSelEnd; 554 @GuardedBy("mH") 555 private int mCursorCandStart; 556 @GuardedBy("mH") 557 private int mCursorCandEnd; 558 @GuardedBy("mH") 559 private int mInitialSelStart; 560 @GuardedBy("mH") 561 private int mInitialSelEnd; 562 563 /** 564 * Handler for {@link RemoteInputConnectionImpl#getInputConnection()}. 565 */ 566 @GuardedBy("mH") 567 private Handler mServedInputConnectionHandler; 568 569 /** 570 * The instance that has previously been sent to the input method. 571 */ 572 @GuardedBy("mH") 573 private CursorAnchorInfo mCursorAnchorInfo = null; 574 575 /** 576 * A special {@link Matrix} that can be provided by the system when this instance is running 577 * inside a virtual display. 578 * 579 * <p>If this is non-{@code null}, {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} 580 * should be adjusted with this {@link Matrix}.</p> 581 * 582 * <p>{@code null} when not used.</p> 583 */ 584 @GuardedBy("mH") 585 private Matrix mVirtualDisplayToScreenMatrix = null; 586 587 // ----------------------------------------------------------- 588 589 /** 590 * ID of the method we are bound to. 591 * 592 * @deprecated New code should use {@code mCurBindState.mImeId}. 593 */ 594 @Deprecated 595 @GuardedBy("mH") 596 @UnsupportedAppUsage(trackingBug = 236937383, 597 maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 598 publicAlternatives = "Apps should not change behavior based on the currently connected" 599 + " IME. If absolutely needed, use {@link InputMethodInfo#getId()} instead.") 600 String mCurId; 601 602 /** 603 * Kept for {@link UnsupportedAppUsage}. Not officially maintained. 604 * 605 * @deprecated New code should use {@code mCurBindState.mImeSession}. 606 */ 607 @Deprecated 608 @GuardedBy("mH") 609 @Nullable 610 @UnsupportedAppUsage(trackingBug = 236937383, 611 maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 612 publicAlternatives = "Use methods on {@link InputMethodManager} instead.") 613 IInputMethodSession mCurMethod; 614 615 /** 616 * Encapsulates per-binding state from {@link InputBindResult}. 617 */ 618 @GuardedBy("mH") 619 @Nullable 620 private BindState mCurBindState; 621 622 /** 623 * Encapsulates IPCs to the currently connected AccessibilityServices. 624 */ 625 @Nullable 626 @GuardedBy("mH") 627 private final SparseArray<IAccessibilityInputMethodSessionInvoker> 628 mAccessibilityInputMethodSession = new SparseArray<>(); 629 630 @GuardedBy("mH") 631 private InputChannel mCurChannel; 632 @GuardedBy("mH") 633 private ImeInputEventSender mCurSender; 634 635 private static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE = 0x0; 636 637 /** 638 * The monitor mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}. 639 * @deprecated This is kept for {@link UnsupportedAppUsage}. Must not be used. 640 */ 641 @Deprecated 642 @GuardedBy("mH") 643 private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE; 644 645 /** 646 * Applies the IME visibility and listens for other state changes. 647 */ 648 @GuardedBy("mH") 649 private ImeInsetsSourceConsumer mImeInsetsConsumer; 650 651 @GuardedBy("mH") 652 private final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20); 653 @GuardedBy("mH") 654 private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20); 655 656 private final DelegateImpl mDelegate = new DelegateImpl(); 657 658 private static boolean sPreventImeStartupUnlessTextEditor; 659 660 // ----------------------------------------------------------- 661 662 private static final int MSG_DUMP = 1; 663 private static final int MSG_BIND = 2; 664 private static final int MSG_UNBIND = 3; 665 private static final int MSG_SET_ACTIVE = 4; 666 private static final int MSG_SEND_INPUT_EVENT = 5; 667 private static final int MSG_TIMEOUT_INPUT_EVENT = 6; 668 private static final int MSG_FLUSH_INPUT_EVENT = 7; 669 private static final int MSG_REPORT_FULLSCREEN_MODE = 10; 670 private static final int MSG_BIND_ACCESSIBILITY_SERVICE = 11; 671 private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12; 672 private static final int MSG_SET_INTERACTIVE = 13; 673 private static final int MSG_UPDATE_VIRTUAL_DISPLAY_TO_SCREEN_MATRIX = 30; 674 private static final int MSG_ON_SHOW_REQUESTED = 31; 675 676 /** 677 * Calling this will invalidate Local stylus handwriting availability Cache which 678 * forces the next query in any process to recompute the cache. 679 * @hide 680 */ invalidateLocalStylusHandwritingAvailabilityCaches()681 public static void invalidateLocalStylusHandwritingAvailabilityCaches() { 682 PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STYLUS_HANDWRITING_PROPERTY); 683 } 684 isAutofillUIShowing(View servedView)685 private static boolean isAutofillUIShowing(View servedView) { 686 AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class); 687 return afm != null && afm.isAutofillUiShowing(); 688 } 689 690 /** 691 * Returns fallback {@link InputMethodManager} if the called one is not likely to be compatible 692 * with the given {@code view}. 693 * 694 * @param view {@link View} to be checked. 695 * @return {@code null} when it is unnecessary (or impossible) to use fallback 696 * {@link InputMethodManager} to which IME API calls need to be re-dispatched. 697 * Non-{@code null} {@link InputMethodManager} if this method believes it'd be safer to 698 * re-dispatch IME APIs calls on it. 699 */ 700 @Nullable getFallbackInputMethodManagerIfNecessary(@ullable View view)701 private InputMethodManager getFallbackInputMethodManagerIfNecessary(@Nullable View view) { 702 if (view == null) { 703 return null; 704 } 705 // As evidenced in Bug 118341760, view.getViewRootImpl().getDisplayId() is supposed to be 706 // more reliable to determine with which display the given view is interacting than 707 // view.getContext().getDisplayId() / view.getContext().getSystemService(), which can be 708 // easily messed up by app developers (or library authors) by creating inconsistent 709 // ContextWrapper objects that re-dispatch those methods to other Context such as 710 // ApplicationContext. 711 final ViewRootImpl viewRootImpl = view.getViewRootImpl(); 712 if (viewRootImpl == null) { 713 return null; 714 } 715 final int viewRootDisplayId = viewRootImpl.getDisplayId(); 716 if (viewRootDisplayId == mDisplayId) { 717 // Expected case. Good to go. 718 return null; 719 } 720 final InputMethodManager fallbackImm = 721 viewRootImpl.mContext.getSystemService(InputMethodManager.class); 722 if (fallbackImm == null) { 723 Log.v(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view); 724 return null; 725 } 726 if (fallbackImm.mDisplayId != viewRootDisplayId) { 727 Log.v(TAG, "b/117267690: Failed to get fallback IMM with expected displayId=" 728 + viewRootDisplayId + " actual IMM#displayId=" + fallbackImm.mDisplayId 729 + " view=" + view); 730 return null; 731 } 732 Log.v(TAG, "b/117267690: Display ID mismatch found." 733 + " ViewRootImpl displayId=" + viewRootDisplayId 734 + " InputMethodManager displayId=" + mDisplayId 735 + ". Use the right InputMethodManager instance to avoid performance overhead.", 736 new Throwable()); 737 return fallbackImm; 738 } 739 740 /** 741 * An internal API that returns the {@link Context} of the current served view connected to 742 * an input method. 743 * @hide 744 */ getFallbackContextFromServedView()745 Context getFallbackContextFromServedView() { 746 synchronized (mH) { 747 if (mCurRootView == null) { 748 return null; 749 } 750 return mServedView != null ? mServedView.getContext() : null; 751 } 752 } 753 canStartInput(View servedView)754 private static boolean canStartInput(View servedView) { 755 // We can start input ether the servedView has window focus 756 // or the activity is showing autofill ui. 757 return servedView.hasWindowFocus() || isAutofillUIShowing(servedView); 758 } 759 760 /** 761 * Reports whether the IME is currently perceptible or not, according to the leash applied by 762 * {@link android.view.WindowInsetsController}. 763 * @hide 764 */ reportPerceptible(@onNull IBinder windowToken, boolean perceptible)765 public void reportPerceptible(@NonNull IBinder windowToken, boolean perceptible) { 766 IInputMethodManagerGlobalInvoker.reportPerceptibleAsync(windowToken, perceptible); 767 } 768 769 private final class DelegateImpl implements 770 ImeFocusController.InputMethodManagerDelegate { 771 772 @Override onPreWindowGainedFocus(ViewRootImpl viewRootImpl)773 public void onPreWindowGainedFocus(ViewRootImpl viewRootImpl) { 774 synchronized (mH) { 775 setCurrentRootViewLocked(viewRootImpl); 776 mCurRootViewWindowFocused = true; 777 } 778 } 779 780 @Override onPostWindowGainedFocus(View viewForWindowFocus, @NonNull WindowManager.LayoutParams windowAttribute)781 public void onPostWindowGainedFocus(View viewForWindowFocus, 782 @NonNull WindowManager.LayoutParams windowAttribute) { 783 boolean forceFocus = false; 784 synchronized (mH) { 785 // Update mNextServedView when focusedView changed. 786 onViewFocusChangedInternal(viewForWindowFocus, true); 787 788 // Starting new input when the next focused view is same as served view but the 789 // currently active connection (if any) is not associated with it. 790 final boolean nextFocusIsServedView = mServedView == viewForWindowFocus; 791 792 if (nextFocusIsServedView 793 && !hasActiveInputConnectionInternal(viewForWindowFocus)) { 794 forceFocus = true; 795 } 796 } 797 798 final int softInputMode = windowAttribute.softInputMode; 799 final int windowFlags = windowAttribute.flags; 800 801 int startInputFlags = getStartInputFlags(viewForWindowFocus, 0); 802 startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS; 803 804 ImeTracing.getInstance().triggerClientDump( 805 "InputMethodManager.DelegateImpl#startInputAsyncOnWindowFocusGain", 806 InputMethodManager.this, null /* icProto */); 807 808 boolean checkFocusResult; 809 synchronized (mH) { 810 if (mCurRootView == null) { 811 return; 812 } 813 if (mRestartOnNextWindowFocus) { 814 if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus as true"); 815 mRestartOnNextWindowFocus = false; 816 forceFocus = true; 817 } 818 checkFocusResult = checkFocusInternalLocked(forceFocus, mCurRootView); 819 } 820 821 if (checkFocusResult) { 822 // We need to restart input on the current focus view. This 823 // should be done in conjunction with telling the system service 824 // about the window gaining focus, to help make the transition 825 // smooth. 826 if (startInputOnWindowFocusGainInternal(StartInputReason.WINDOW_FOCUS_GAIN, 827 viewForWindowFocus, startInputFlags, softInputMode, windowFlags)) { 828 return; 829 } 830 } 831 832 synchronized (mH) { 833 // For some reason we didn't do a startInput + windowFocusGain, so 834 // we'll just do a window focus gain and call it a day. 835 if (DEBUG) { 836 Log.v(TAG, "Reporting focus gain, without startInput"); 837 } 838 839 // ignore the result 840 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus"); 841 IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus( 842 StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, 843 viewForWindowFocus.getWindowToken(), startInputFlags, softInputMode, 844 windowFlags, 845 null, 846 null, null, 847 mCurRootView.mContext.getApplicationInfo().targetSdkVersion, 848 UserHandle.myUserId(), mImeDispatcher); 849 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 850 } 851 } 852 853 @Override onWindowLostFocus(@onNull ViewRootImpl viewRootImpl)854 public void onWindowLostFocus(@NonNull ViewRootImpl viewRootImpl) { 855 synchronized (mH) { 856 if (mCurRootView == viewRootImpl) { 857 mCurRootViewWindowFocused = false; 858 859 clearCurRootViewIfNeeded(); 860 } 861 } 862 } 863 864 @Override onViewFocusChanged(@ullable View view, boolean hasFocus)865 public void onViewFocusChanged(@Nullable View view, boolean hasFocus) { 866 onViewFocusChangedInternal(view, hasFocus); 867 } 868 869 @Override onScheduledCheckFocus(ViewRootImpl viewRootImpl)870 public void onScheduledCheckFocus(ViewRootImpl viewRootImpl) { 871 synchronized (mH) { 872 if (!checkFocusInternalLocked(false, viewRootImpl)) { 873 return; 874 } 875 } 876 startInputOnWindowFocusGainInternal(StartInputReason.SCHEDULED_CHECK_FOCUS, 877 null /* focusedView */, 0 /* startInputFlags */, 0 /* softInputMode */, 878 0 /* windowFlags */); 879 } 880 881 @Override onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl)882 public void onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl) { 883 synchronized (mH) { 884 if (mCurRootView != view.getViewRootImpl()) { 885 return; 886 } 887 if (mNextServedView == view) { 888 mNextServedView = null; 889 } 890 if (mServedView == view) { 891 viewRootImpl.dispatchCheckFocus(); 892 } 893 } 894 } 895 896 @Override onWindowDismissed(ViewRootImpl viewRootImpl)897 public void onWindowDismissed(ViewRootImpl viewRootImpl) { 898 synchronized (mH) { 899 if (mCurRootView != viewRootImpl) { 900 return; 901 } 902 if (mServedView != null) { 903 finishInputLocked(); 904 } 905 setCurrentRootViewLocked(null); 906 } 907 } 908 909 @GuardedBy("mH") setCurrentRootViewLocked(ViewRootImpl rootView)910 private void setCurrentRootViewLocked(ViewRootImpl rootView) { 911 mImeDispatcher.switchRootView(mCurRootView, rootView); 912 mCurRootView = rootView; 913 } 914 } 915 916 /** @hide */ getDelegate()917 public DelegateImpl getDelegate() { 918 return mDelegate; 919 } 920 921 /** 922 * Checks whether the active input connection (if any) is for the given view. 923 * 924 * <p>Note that {@code view} parameter does not take 925 * {@link View#checkInputConnectionProxy(View)} into account. This method returns {@code true} 926 * when and only when the specified {@code view} is the actual {@link View} instance that is 927 * connected to the IME.</p> 928 * 929 * @param view {@link View} to be checked. 930 * @return {@code true} if {@code view} is currently interacting with IME. 931 * @hide 932 */ 933 @TestApi hasActiveInputConnection(@ullable View view)934 public boolean hasActiveInputConnection(@Nullable View view) { 935 synchronized (mH) { 936 return mCurRootView != null 937 && view != null 938 && mServedView == view 939 && mServedInputConnection != null 940 && mServedInputConnection.isAssociatedWith(view) 941 && isImeSessionAvailableLocked(); 942 } 943 } 944 945 /** 946 * Checks whether the active input connection (if any) is for the given view. 947 * 948 * Note that this method is only intended for restarting input after focus gain 949 * (e.g. b/160391516), DO NOT leverage this method to do another check. 950 */ hasActiveInputConnectionInternal(@ullable View view)951 private boolean hasActiveInputConnectionInternal(@Nullable View view) { 952 synchronized (mH) { 953 if (!hasServedByInputMethodLocked(view) || !isImeSessionAvailableLocked()) { 954 return false; 955 } 956 957 return mServedInputConnection != null 958 && mServedInputConnection.isAssociatedWith(view); 959 } 960 } 961 startInputOnWindowFocusGainInternal(@tartInputReason int startInputReason, View focusedView, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags)962 private boolean startInputOnWindowFocusGainInternal(@StartInputReason int startInputReason, 963 View focusedView, @StartInputFlags int startInputFlags, 964 @SoftInputModeFlags int softInputMode, int windowFlags) { 965 synchronized (mH) { 966 mCurrentEditorInfo = null; 967 mCompletions = null; 968 mServedConnecting = true; 969 } 970 return startInputInner(startInputReason, 971 focusedView != null ? focusedView.getWindowToken() : null, startInputFlags, 972 softInputMode, windowFlags); 973 } 974 975 @GuardedBy("mH") getServedViewLocked()976 private View getServedViewLocked() { 977 return mCurRootView != null ? mServedView : null; 978 } 979 980 @GuardedBy("mH") getNextServedViewLocked()981 private View getNextServedViewLocked() { 982 return mCurRootView != null ? mNextServedView : null; 983 } 984 985 /** 986 * Returns {@code true} when the given view has been served by Input Method. 987 */ 988 @GuardedBy("mH") hasServedByInputMethodLocked(View view)989 private boolean hasServedByInputMethodLocked(View view) { 990 final View servedView = getServedViewLocked(); 991 return (servedView == view 992 || (servedView != null && servedView.checkInputConnectionProxy(view))); 993 } 994 995 class H extends Handler { H(Looper looper)996 H(Looper looper) { 997 super(looper, null, true); 998 } 999 1000 @Override handleMessage(Message msg)1001 public void handleMessage(Message msg) { 1002 switch (msg.what) { 1003 case MSG_DUMP: { 1004 SomeArgs args = (SomeArgs)msg.obj; 1005 try { 1006 doDump((FileDescriptor)args.arg1, 1007 (PrintWriter)args.arg2, (String[])args.arg3); 1008 } catch (RuntimeException e) { 1009 ((PrintWriter)args.arg2).println("Exception: " + e); 1010 } 1011 synchronized (args.arg4) { 1012 ((CountDownLatch)args.arg4).countDown(); 1013 } 1014 args.recycle(); 1015 return; 1016 } 1017 case MSG_BIND: { 1018 final InputBindResult res = (InputBindResult)msg.obj; 1019 if (DEBUG) { 1020 Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id); 1021 } 1022 synchronized (mH) { 1023 final int curBindSequence = getBindSequenceLocked(); 1024 if (curBindSequence < 0 || curBindSequence != res.sequence) { 1025 Log.w(TAG, "Ignoring onBind: cur seq=" + curBindSequence 1026 + ", given seq=" + res.sequence); 1027 if (res.channel != null && res.channel != mCurChannel) { 1028 res.channel.dispose(); 1029 } 1030 return; 1031 } 1032 1033 mRequestUpdateCursorAnchorInfoMonitorMode = 1034 REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE; 1035 1036 updateInputChannelLocked(res.channel); 1037 mCurMethod = res.method; // for @UnsupportedAppUsage 1038 mCurBindState = new BindState(res); 1039 mCurId = res.id; // for @UnsupportedAppUsage 1040 mVirtualDisplayToScreenMatrix = res.getVirtualDisplayToScreenMatrix(); 1041 } 1042 startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0); 1043 return; 1044 } 1045 case MSG_UNBIND: { 1046 final int sequence = msg.arg1; 1047 @UnbindReason 1048 final int reason = msg.arg2; 1049 if (DEBUG) { 1050 Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence + 1051 " reason=" + InputMethodDebug.unbindReasonToString(reason)); 1052 } 1053 final boolean startInput; 1054 synchronized (mH) { 1055 if (getBindSequenceLocked() != sequence) { 1056 return; 1057 } 1058 clearAllAccessibilityBindingLocked(); 1059 clearBindingLocked(); 1060 // If we were actively using the last input method, then 1061 // we would like to re-connect to the next input method. 1062 final View servedView = getServedViewLocked(); 1063 if (servedView != null && servedView.isFocused()) { 1064 mServedConnecting = true; 1065 } 1066 startInput = mActive; 1067 } 1068 if (startInput) { 1069 startInputInner( 1070 StartInputReason.UNBOUND_FROM_IMMS, null, 0, 0, 0); 1071 } 1072 return; 1073 } 1074 case MSG_BIND_ACCESSIBILITY_SERVICE: { 1075 final int id = msg.arg1; 1076 final InputBindResult res = (InputBindResult) msg.obj; 1077 if (DEBUG) { 1078 Log.i(TAG, "handleMessage: MSG_BIND_ACCESSIBILITY " + res.sequence 1079 + "," + res.id); 1080 } 1081 synchronized (mH) { 1082 final int curBindSequence = getBindSequenceLocked(); 1083 if (curBindSequence < 0 || curBindSequence != res.sequence) { 1084 Log.w(TAG, "Ignoring onBind: cur seq=" + curBindSequence 1085 + ", given seq=" + res.sequence); 1086 if (res.channel != null && res.channel != mCurChannel) { 1087 res.channel.dispose(); 1088 } 1089 return; 1090 } 1091 1092 // Since IMM can start inputting text before a11y sessions are back, 1093 // we send a notification so that the a11y service knows the session is 1094 // registered and update the a11y service with the current cursor positions. 1095 if (res.accessibilitySessions != null) { 1096 IAccessibilityInputMethodSessionInvoker invoker = 1097 IAccessibilityInputMethodSessionInvoker.createOrNull( 1098 res.accessibilitySessions.get(id)); 1099 if (invoker != null) { 1100 mAccessibilityInputMethodSession.put(id, invoker); 1101 if (mServedInputConnection != null) { 1102 invoker.updateSelection(mInitialSelStart, mInitialSelEnd, 1103 mCursorSelStart, mCursorSelEnd, mCursorCandStart, 1104 mCursorCandEnd); 1105 } else { 1106 // If an a11y service binds before input starts, we should still 1107 // send a notification because the a11y service doesn't know it 1108 // binds before or after input starts, it may wonder if it binds 1109 // after input starts, why it doesn't receive a notification of 1110 // the current cursor positions. 1111 invoker.updateSelection(-1, -1, -1, -1, -1, -1); 1112 } 1113 } 1114 } 1115 } 1116 startInputInner(StartInputReason.BOUND_ACCESSIBILITY_SESSION_TO_IMMS, null, 1117 0, 0, 0); 1118 return; 1119 } 1120 case MSG_UNBIND_ACCESSIBILITY_SERVICE: { 1121 final int sequence = msg.arg1; 1122 final int id = msg.arg2; 1123 if (DEBUG) { 1124 Log.i(TAG, "handleMessage: MSG_UNBIND_ACCESSIBILITY_SERVICE " 1125 + sequence + " id=" + id); 1126 } 1127 synchronized (mH) { 1128 if (getBindSequenceLocked() != sequence) { 1129 if (DEBUG) { 1130 Log.i(TAG, "current BindSequence =" + getBindSequenceLocked() 1131 + " sequence =" + sequence + " id=" + id); 1132 } 1133 return; 1134 } 1135 clearAccessibilityBindingLocked(id); 1136 } 1137 return; 1138 } 1139 case MSG_SET_ACTIVE: { 1140 final boolean active = msg.arg1 != 0; 1141 final boolean fullscreen = msg.arg2 != 0; 1142 if (DEBUG) { 1143 Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive); 1144 } 1145 synchronized (mH) { 1146 mActive = active; 1147 mFullscreenMode = fullscreen; 1148 1149 if (!active) { 1150 // Some other client has starting using the IME, so note 1151 // that this happened and make sure our own editor's 1152 // state is reset. 1153 mRestartOnNextWindowFocus = true; 1154 // Note that finishComposingText() is allowed to run 1155 // even when we are not active. 1156 mFallbackInputConnection.finishComposingTextFromImm(); 1157 1158 if (clearCurRootViewIfNeeded()) { 1159 return; 1160 } 1161 } 1162 // Check focus again in case that "onWindowFocus" is called before 1163 // handling this message. 1164 final View servedView = getServedViewLocked(); 1165 if (servedView == null || !canStartInput(servedView)) { 1166 return; 1167 } 1168 if (mCurRootView == null) { 1169 return; 1170 } 1171 if (!checkFocusInternalLocked(mRestartOnNextWindowFocus, mCurRootView)) { 1172 return; 1173 } 1174 mCurrentEditorInfo = null; 1175 mCompletions = null; 1176 mServedConnecting = true; 1177 } 1178 final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS 1179 : StartInputReason.DEACTIVATED_BY_IMMS; 1180 startInputInner(reason, null, 0, 0, 0); 1181 return; 1182 } 1183 case MSG_SET_INTERACTIVE: { 1184 final boolean interactive = msg.arg1 != 0; 1185 final boolean fullscreen = msg.arg2 != 0; 1186 if (DEBUG) { 1187 Log.i(TAG, "handleMessage: MSG_SET_INTERACTIVE " + interactive 1188 + ", was " + mActive); 1189 } 1190 synchronized (mH) { 1191 mActive = interactive; 1192 mFullscreenMode = fullscreen; 1193 if (interactive) { 1194 final View rootView = 1195 mCurRootView != null ? mCurRootView.getView() : null; 1196 if (rootView == null) { 1197 return; 1198 } 1199 // Find the next view focus to start the input connection when the 1200 // device was interactive. 1201 final ViewRootImpl currentViewRootImpl = mCurRootView; 1202 rootView.post(() -> { 1203 synchronized (mH) { 1204 if (mCurRootView != currentViewRootImpl) { 1205 return; 1206 } 1207 } 1208 final View focusedView = currentViewRootImpl.getView().findFocus(); 1209 onViewFocusChangedInternal(focusedView, focusedView != null); 1210 }); 1211 } else { 1212 finishInputLocked(); 1213 if (isImeSessionAvailableLocked()) { 1214 mCurBindState.mImeSession.finishInput(); 1215 } 1216 forAccessibilitySessionsLocked( 1217 IAccessibilityInputMethodSessionInvoker::finishInput); 1218 } 1219 } 1220 return; 1221 } 1222 case MSG_SEND_INPUT_EVENT: { 1223 sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj); 1224 return; 1225 } 1226 case MSG_TIMEOUT_INPUT_EVENT: { 1227 finishedInputEvent(msg.arg1, false, true); 1228 return; 1229 } 1230 case MSG_FLUSH_INPUT_EVENT: { 1231 finishedInputEvent(msg.arg1, false, false); 1232 return; 1233 } 1234 case MSG_REPORT_FULLSCREEN_MODE: { 1235 final boolean fullscreen = msg.arg1 != 0; 1236 RemoteInputConnectionImpl ic = null; 1237 synchronized (mH) { 1238 if (mFullscreenMode != fullscreen && mServedInputConnection != null) { 1239 ic = mServedInputConnection; 1240 mFullscreenMode = fullscreen; 1241 } 1242 } 1243 if (ic != null) { 1244 ic.dispatchReportFullscreenMode(fullscreen); 1245 } 1246 return; 1247 } 1248 case MSG_UPDATE_VIRTUAL_DISPLAY_TO_SCREEN_MATRIX: { 1249 final float[] matrixValues = (float[]) msg.obj; 1250 final int bindSequence = msg.arg1; 1251 synchronized (mH) { 1252 if (getBindSequenceLocked() != bindSequence) { 1253 return; 1254 } 1255 if (matrixValues == null || mVirtualDisplayToScreenMatrix == null) { 1256 // Either InputBoundResult#mVirtualDisplayToScreenMatrixValues is null 1257 // OR this app is unbound from the parent VirtualDisplay. In this case, 1258 // calling updateCursorAnchorInfo() isn't safe. Only clear the matrix. 1259 mVirtualDisplayToScreenMatrix = null; 1260 return; 1261 } 1262 1263 final float[] currentValues = new float[9]; 1264 mVirtualDisplayToScreenMatrix.getValues(currentValues); 1265 if (Arrays.equals(currentValues, matrixValues)) { 1266 return; 1267 } 1268 mVirtualDisplayToScreenMatrix.setValues(matrixValues); 1269 1270 if (mCursorAnchorInfo == null || !isImeSessionAvailableLocked() 1271 || mServedInputConnection == null) { 1272 return; 1273 } 1274 if (!mServedInputConnection.isCursorAnchorInfoMonitoring()) { 1275 return; 1276 } 1277 // Since the host VirtualDisplay is moved, we need to issue 1278 // IMS#updateCursorAnchorInfo() again. 1279 mCurBindState.mImeSession.updateCursorAnchorInfo( 1280 CursorAnchorInfo.createForAdditionalParentMatrix( 1281 mCursorAnchorInfo, mVirtualDisplayToScreenMatrix)); 1282 } 1283 return; 1284 } 1285 case MSG_ON_SHOW_REQUESTED: { 1286 synchronized (mH) { 1287 if (mImeInsetsConsumer != null) { 1288 mImeInsetsConsumer.onShowRequested(); 1289 } 1290 } 1291 return; 1292 } 1293 } 1294 } 1295 } 1296 1297 private final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { 1298 @Override 1299 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 1300 // No need to check for dump permission, since we only give this 1301 // interface to the system. 1302 CountDownLatch latch = new CountDownLatch(1); 1303 SomeArgs sargs = SomeArgs.obtain(); 1304 sargs.arg1 = fd; 1305 sargs.arg2 = fout; 1306 sargs.arg3 = args; 1307 sargs.arg4 = latch; 1308 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs)); 1309 try { 1310 if (!latch.await(5, TimeUnit.SECONDS)) { 1311 fout.println("Timeout waiting for dump"); 1312 } 1313 } catch (InterruptedException e) { 1314 fout.println("Interrupted waiting for dump"); 1315 } 1316 } 1317 1318 @Override 1319 public void onBindMethod(InputBindResult res) { 1320 mH.obtainMessage(MSG_BIND, res).sendToTarget(); 1321 } 1322 1323 @Override 1324 public void onBindAccessibilityService(InputBindResult res, int id) { 1325 mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget(); 1326 } 1327 1328 @Override 1329 public void onUnbindMethod(int sequence, @UnbindReason int unbindReason) { 1330 mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget(); 1331 } 1332 1333 @Override 1334 public void onUnbindAccessibilityService(int sequence, int id) { 1335 mH.obtainMessage(MSG_UNBIND_ACCESSIBILITY_SERVICE, sequence, id).sendToTarget(); 1336 } 1337 1338 @Override 1339 public void setActive(boolean active, boolean fullscreen) { 1340 mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0).sendToTarget(); 1341 } 1342 1343 @Override 1344 public void setInteractive(boolean interactive, boolean fullscreen) { 1345 mH.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, fullscreen ? 1 : 0) 1346 .sendToTarget(); 1347 } 1348 1349 @Override 1350 public void scheduleStartInputIfNecessary(boolean fullscreen) { 1351 // TODO(b/149859205): See if we can optimize this by having a fused dedicated operation. 1352 mH.obtainMessage(MSG_SET_ACTIVE, 0 /* active */, fullscreen ? 1 : 0).sendToTarget(); 1353 mH.obtainMessage(MSG_SET_ACTIVE, 1 /* active */, fullscreen ? 1 : 0).sendToTarget(); 1354 } 1355 1356 @Override 1357 public void reportFullscreenMode(boolean fullscreen) { 1358 mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0) 1359 .sendToTarget(); 1360 } 1361 1362 @Override 1363 public void updateVirtualDisplayToScreenMatrix(int bindSequence, float[] matrixValues) { 1364 mH.obtainMessage(MSG_UPDATE_VIRTUAL_DISPLAY_TO_SCREEN_MATRIX, bindSequence, 0, 1365 matrixValues).sendToTarget(); 1366 } 1367 1368 @Override 1369 public void setImeTraceEnabled(boolean enabled) { 1370 ImeTracing.getInstance().setEnabled(enabled); 1371 } 1372 1373 @Override 1374 public void throwExceptionFromSystem(String message) { 1375 throw new RuntimeException(message); 1376 } 1377 }; 1378 1379 /** 1380 * For layoutlib to clean up static objects inside {@link InputMethodManager}. 1381 */ tearDownEditMode()1382 static void tearDownEditMode() { 1383 if (!isInEditMode()) { 1384 throw new UnsupportedOperationException( 1385 "This method must be called only from layoutlib"); 1386 } 1387 synchronized (sLock) { 1388 sInstance = null; 1389 } 1390 } 1391 1392 /** 1393 * For layoutlib to override this method to return {@code true}. 1394 * 1395 * @return {@code true} if the process is running for developer tools 1396 * @see View#isInEditMode() 1397 */ isInEditMode()1398 private static boolean isInEditMode() { 1399 return false; 1400 } 1401 isInEditModeInternal()1402 static boolean isInEditModeInternal() { 1403 return isInEditMode(); 1404 } 1405 1406 @NonNull createInstance(int displayId, Looper looper)1407 private static InputMethodManager createInstance(int displayId, Looper looper) { 1408 return isInEditMode() ? createStubInstance(displayId, looper) 1409 : createRealInstance(displayId, looper); 1410 } 1411 1412 @NonNull createRealInstance(int displayId, Looper looper)1413 private static InputMethodManager createRealInstance(int displayId, Looper looper) { 1414 final IInputMethodManager service = IInputMethodManagerGlobalInvoker.getService(); 1415 if (service == null) { 1416 throw new IllegalStateException("IInputMethodManager is not available"); 1417 } 1418 final InputMethodManager imm = new InputMethodManager(service, displayId, looper); 1419 // InputMethodManagerService#addClient() relies on Binder.getCalling{Pid, Uid}() to 1420 // associate PID/UID with each IME client. This means: 1421 // A. if this method call will be handled as an IPC, there is no problem. 1422 // B. if this method call will be handled as an in-proc method call, we need to 1423 // ensure that Binder.getCalling{Pid, Uid}() return Process.my{Pid, Uid}() 1424 // Either ways we can always call Binder.{clear, restore}CallingIdentity() because 1425 // 1) doing so has no effect for A and 2) doing so is sufficient for B. 1426 final long identity = Binder.clearCallingIdentity(); 1427 try { 1428 IInputMethodManagerGlobalInvoker.addClient(imm.mClient, imm.mFallbackInputConnection, 1429 displayId); 1430 } finally { 1431 Binder.restoreCallingIdentity(identity); 1432 } 1433 return imm; 1434 } 1435 1436 @NonNull createStubInstance(int displayId, Looper looper)1437 private static InputMethodManager createStubInstance(int displayId, Looper looper) { 1438 // If InputMethodManager is running for layoutlib, stub out IPCs into IMMS. 1439 final Class<IInputMethodManager> c = IInputMethodManager.class; 1440 final IInputMethodManager stubInterface = 1441 (IInputMethodManager) Proxy.newProxyInstance(c.getClassLoader(), 1442 new Class[]{c}, (proxy, method, args) -> { 1443 final Class<?> returnType = method.getReturnType(); 1444 if (returnType == boolean.class) { 1445 return false; 1446 } else if (returnType == int.class) { 1447 return 0; 1448 } else if (returnType == long.class) { 1449 return 0L; 1450 } else if (returnType == short.class) { 1451 return 0; 1452 } else if (returnType == char.class) { 1453 return 0; 1454 } else if (returnType == byte.class) { 1455 return 0; 1456 } else if (returnType == float.class) { 1457 return 0f; 1458 } else if (returnType == double.class) { 1459 return 0.0; 1460 } else { 1461 return null; 1462 } 1463 }); 1464 return new InputMethodManager(stubInterface, displayId, looper); 1465 } 1466 InputMethodManager(@onNull IInputMethodManager service, int displayId, Looper looper)1467 private InputMethodManager(@NonNull IInputMethodManager service, int displayId, Looper looper) { 1468 mService = service; // For @UnsupportedAppUsage 1469 mMainLooper = looper; 1470 mH = new H(looper); 1471 mDisplayId = displayId; 1472 mFallbackInputConnection = new RemoteInputConnectionImpl(looper, 1473 new BaseInputConnection(this, false), this, null); 1474 } 1475 1476 /** 1477 * Retrieve an instance for the given {@link Context}, creating it if it doesn't already exist. 1478 * 1479 * @param context {@link Context} for which IME APIs need to work 1480 * @return {@link InputMethodManager} instance 1481 * @hide 1482 */ 1483 @NonNull forContext(@isplayContext Context context)1484 public static InputMethodManager forContext(@DisplayContext Context context) { 1485 final int displayId = context.getDisplayId(); 1486 // For better backward compatibility, we always use Looper.getMainLooper() for the default 1487 // display case. 1488 final Looper looper = displayId == Display.DEFAULT_DISPLAY 1489 ? Looper.getMainLooper() : context.getMainLooper(); 1490 // Keep track of whether to expect the IME to be unavailable so as to avoid log spam in 1491 // sendInputEventOnMainLooperLocked() by not logging a verbose message on every DPAD event 1492 sPreventImeStartupUnlessTextEditor = context.getResources().getBoolean( 1493 com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor); 1494 return forContextInternal(displayId, looper); 1495 } 1496 1497 @NonNull forContextInternal(int displayId, Looper looper)1498 private static InputMethodManager forContextInternal(int displayId, Looper looper) { 1499 final boolean isDefaultDisplay = displayId == Display.DEFAULT_DISPLAY; 1500 synchronized (sLock) { 1501 InputMethodManager instance = sInstanceMap.get(displayId); 1502 if (instance != null) { 1503 return instance; 1504 } 1505 instance = createInstance(displayId, looper); 1506 // For backward compatibility, store the instance also to sInstance for default display. 1507 if (sInstance == null && isDefaultDisplay) { 1508 sInstance = instance; 1509 } 1510 sInstanceMap.put(displayId, instance); 1511 return instance; 1512 } 1513 } 1514 1515 /** 1516 * Deprecated. Do not use. 1517 * 1518 * @return global {@link InputMethodManager} instance 1519 * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully 1520 * support multi-display scenario. 1521 * @hide 1522 */ 1523 @Deprecated 1524 @UnsupportedAppUsage getInstance()1525 public static InputMethodManager getInstance() { 1526 Log.w(TAG, "InputMethodManager.getInstance() is deprecated because it cannot be" 1527 + " compatible with multi-display." 1528 + " Use context.getSystemService(InputMethodManager.class) instead.", 1529 new Throwable()); 1530 ensureDefaultInstanceForDefaultDisplayIfNecessary(); 1531 return peekInstance(); 1532 } 1533 1534 /** 1535 * Deprecated. Do not use. 1536 * 1537 * @return {@link #sInstance} 1538 * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully 1539 * support multi-display scenario. 1540 * @hide 1541 */ 1542 @Deprecated 1543 @UnsupportedAppUsage peekInstance()1544 public static InputMethodManager peekInstance() { 1545 Log.w(TAG, "InputMethodManager.peekInstance() is deprecated because it cannot be" 1546 + " compatible with multi-display." 1547 + " Use context.getSystemService(InputMethodManager.class) instead.", 1548 new Throwable()); 1549 synchronized (sLock) { 1550 return sInstance; 1551 } 1552 } 1553 1554 /** 1555 * Returns the list of installed input methods. 1556 * 1557 * <p>On multi user environment, this API returns a result for the calling process user.</p> 1558 * 1559 * @return {@link List} of {@link InputMethodInfo}. 1560 */ 1561 @NonNull getInputMethodList()1562 public List<InputMethodInfo> getInputMethodList() { 1563 // We intentionally do not use UserHandle.getCallingUserId() here because for system 1564 // services InputMethodManagerInternal.getInputMethodListAsUser() should be used 1565 // instead. 1566 return IInputMethodManagerGlobalInvoker.getInputMethodList(UserHandle.myUserId(), 1567 DirectBootAwareness.AUTO); 1568 } 1569 1570 /** 1571 * Returns {@code true} if currently selected IME supports Stylus handwriting & is enabled. 1572 * If the method returns {@code false}, {@link #startStylusHandwriting(View)} shouldn't be 1573 * called and Stylus touch should continue as normal touch input. 1574 * @see #startStylusHandwriting(View) 1575 */ isStylusHandwritingAvailable()1576 public boolean isStylusHandwritingAvailable() { 1577 return isStylusHandwritingAvailableAsUser(UserHandle.myUserId()); 1578 } 1579 1580 /** 1581 * Returns {@code true} if currently selected IME supports Stylus handwriting & is enabled for 1582 * the given userId. 1583 * 1584 * <p>If the method returns {@code false}, {@link #startStylusHandwriting(View)} shouldn't be 1585 * called and Stylus touch should continue as normal touch input.</p> 1586 * 1587 * <p>{@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required when and only when 1588 * {@code userId} is different from the user id of the current process.</p> 1589 * 1590 * @see #startStylusHandwriting(View) 1591 * @param userId user ID to query. 1592 * @hide 1593 */ 1594 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) isStylusHandwritingAvailableAsUser(@serIdInt int userId)1595 public boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId) { 1596 final Context fallbackContext = ActivityThread.currentApplication(); 1597 if (fallbackContext == null) { 1598 return false; 1599 } 1600 boolean isAvailable; 1601 synchronized (mH) { 1602 if (mStylusHandwritingAvailableCache == null) { 1603 mStylusHandwritingAvailableCache = new PropertyInvalidatedCache<>( 1604 4 /* maxEntries */, CACHE_KEY_STYLUS_HANDWRITING_PROPERTY) { 1605 @Override 1606 public Boolean recompute(Integer userId) { 1607 return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser( 1608 userId); 1609 } 1610 }; 1611 } 1612 isAvailable = mStylusHandwritingAvailableCache.query(userId); 1613 } 1614 return isAvailable; 1615 } 1616 1617 /** 1618 * Returns the list of installed input methods for the specified user. 1619 * 1620 * <p>{@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required when and only when 1621 * {@code userId} is different from the user id of the current process.</p> 1622 * 1623 * @param userId user ID to query 1624 * @return {@link List} of {@link InputMethodInfo}. 1625 * @hide 1626 */ 1627 @TestApi 1628 @NonNull 1629 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) getInputMethodListAsUser(@serIdInt int userId)1630 public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) { 1631 return IInputMethodManagerGlobalInvoker.getInputMethodList(userId, 1632 DirectBootAwareness.AUTO); 1633 } 1634 1635 /** 1636 * Returns the list of installed input methods for the specified user. 1637 * 1638 * <p>{@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required when and only when 1639 * {@code userId} is different from the user id of the current process.</p> 1640 * 1641 * @param userId user ID to query 1642 * @param directBootAwareness {@code true} if caller want to query installed input methods list 1643 * on user locked state. 1644 * @return {@link List} of {@link InputMethodInfo}. 1645 * @hide 1646 */ 1647 @NonNull 1648 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) getInputMethodListAsUser(@serIdInt int userId, @DirectBootAwareness int directBootAwareness)1649 public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId, 1650 @DirectBootAwareness int directBootAwareness) { 1651 return IInputMethodManagerGlobalInvoker.getInputMethodList(userId, directBootAwareness); 1652 } 1653 1654 /** 1655 * Returns the {@link InputMethodInfo} of the currently selected input method (for the process's 1656 * user). 1657 * 1658 * <p>On multi user environment, this API returns a result for the calling process user.</p> 1659 */ 1660 @Nullable getCurrentInputMethodInfo()1661 public InputMethodInfo getCurrentInputMethodInfo() { 1662 // We intentionally do not use UserHandle.getCallingUserId() here because for system 1663 // services InputMethodManagerInternal.getCurrentInputMethodInfoForUser() should be used 1664 // instead. 1665 return IInputMethodManagerGlobalInvoker.getCurrentInputMethodInfoAsUser( 1666 UserHandle.myUserId()); 1667 } 1668 1669 /** 1670 * Returns the {@link InputMethodInfo} for currently selected input method for the given user. 1671 * 1672 * @param user user to query. 1673 * @hide 1674 */ 1675 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL) 1676 @Nullable 1677 @SystemApi 1678 @SuppressLint("UserHandle") getCurrentInputMethodInfoAsUser(@onNull UserHandle user)1679 public InputMethodInfo getCurrentInputMethodInfoAsUser(@NonNull UserHandle user) { 1680 Objects.requireNonNull(user); 1681 return IInputMethodManagerGlobalInvoker.getCurrentInputMethodInfoAsUser( 1682 user.getIdentifier()); 1683 } 1684 1685 /** 1686 * Returns the list of enabled input methods. 1687 * 1688 * <p>On multi user environment, this API returns a result for the calling process user.</p> 1689 * 1690 * @return {@link List} of {@link InputMethodInfo}. 1691 */ 1692 @NonNull getEnabledInputMethodList()1693 public List<InputMethodInfo> getEnabledInputMethodList() { 1694 // We intentionally do not use UserHandle.getCallingUserId() here because for system 1695 // services InputMethodManagerInternal.getEnabledInputMethodListAsUser() should be used 1696 // instead. 1697 return IInputMethodManagerGlobalInvoker.getEnabledInputMethodList(UserHandle.myUserId()); 1698 } 1699 1700 /** 1701 * Returns the list of enabled input methods for the specified user. 1702 * 1703 * <p>{@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required when and only when 1704 * {@code userId} is different from the user id of the current process.</p> 1705 * 1706 * @param userId user ID to query 1707 * @return {@link List} of {@link InputMethodInfo}. 1708 * @see #getEnabledInputMethodSubtypeListAsUser(String, boolean, int) 1709 * @hide 1710 */ 1711 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) getEnabledInputMethodListAsUser(@serIdInt int userId)1712 public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) { 1713 return IInputMethodManagerGlobalInvoker.getEnabledInputMethodList(userId); 1714 } 1715 1716 /** 1717 * Returns a list of enabled input method subtypes for the specified input method info. 1718 * 1719 * <p>On multi user environment, this API returns a result for the calling process user.</p> 1720 * 1721 * @param imi The {@link InputMethodInfo} whose subtypes list will be returned. If {@code null}, 1722 * returns enabled subtypes for the currently selected {@link InputMethodInfo}. 1723 * @param allowsImplicitlyEnabledSubtypes A boolean flag to allow to return the implicitly 1724 * enabled subtypes. If an input method info doesn't have enabled subtypes, the framework 1725 * will implicitly enable subtypes according to the current system language. 1726 */ 1727 @NonNull getEnabledInputMethodSubtypeList(@ullable InputMethodInfo imi, boolean allowsImplicitlyEnabledSubtypes)1728 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(@Nullable InputMethodInfo imi, 1729 boolean allowsImplicitlyEnabledSubtypes) { 1730 return IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList( 1731 imi == null ? null : imi.getId(), 1732 allowsImplicitlyEnabledSubtypes, 1733 UserHandle.myUserId()); 1734 } 1735 1736 /** 1737 * Returns a list of enabled input method subtypes for the specified input method info for the 1738 * specified user. 1739 * 1740 * @param imeId IME ID to be queried about. 1741 * @param allowsImplicitlyEnabledSubtypes {@code true} to include implicitly enabled subtypes. 1742 * @param userId user ID to be queried about. 1743 * {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required if this is 1744 * different from the calling process user ID. 1745 * @return {@link List} of {@link InputMethodSubtype}. 1746 * @see #getEnabledInputMethodListAsUser(int) 1747 * @hide 1748 */ 1749 @NonNull 1750 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) getEnabledInputMethodSubtypeListAsUser( @onNull String imeId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId)1751 public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser( 1752 @NonNull String imeId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) { 1753 return IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList( 1754 Objects.requireNonNull(imeId), allowsImplicitlyEnabledSubtypes, userId); 1755 } 1756 1757 /** 1758 * @deprecated Use {@link InputMethodService#showStatusIcon(int)} instead. This method was 1759 * intended for IME developers who should be accessing APIs through the service. APIs in this 1760 * class are intended for app developers interacting with the IME. 1761 */ 1762 @Deprecated showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId)1763 public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) { 1764 InputMethodPrivilegedOperationsRegistry.get( 1765 imeToken).updateStatusIconAsync(packageName, iconId); 1766 } 1767 1768 /** 1769 * @deprecated Use {@link InputMethodService#hideStatusIcon()} instead. This method was 1770 * intended for IME developers who should be accessing APIs through the service. APIs in 1771 * this class are intended for app developers interacting with the IME. 1772 */ 1773 @Deprecated hideStatusIcon(IBinder imeToken)1774 public void hideStatusIcon(IBinder imeToken) { 1775 InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIconAsync(null, 0); 1776 } 1777 1778 /** 1779 * This hidden API is deprecated in {@link android.os.Build.VERSION_CODES#Q}. Does nothing. 1780 * 1781 * @param spans will be ignored. 1782 * 1783 * @deprecated Do not use. 1784 * @hide 1785 */ 1786 @Deprecated 1787 @UnsupportedAppUsage registerSuggestionSpansForNotification(SuggestionSpan[] spans)1788 public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) { 1789 Log.w(TAG, "registerSuggestionSpansForNotification() is deprecated. Does nothing."); 1790 } 1791 1792 /** 1793 * This hidden API is deprecated in {@link android.os.Build.VERSION_CODES#Q}. Does nothing. 1794 * 1795 * @deprecated Do not use. 1796 * @hide 1797 */ 1798 @Deprecated 1799 @UnsupportedAppUsage notifySuggestionPicked(SuggestionSpan span, String originalString, int index)1800 public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) { 1801 Log.w(TAG, "notifySuggestionPicked() is deprecated. Does nothing."); 1802 } 1803 1804 /** 1805 * Allows you to discover whether the attached input method is running 1806 * in fullscreen mode. Return true if it is fullscreen, entirely covering 1807 * your UI, else returns false. 1808 */ isFullscreenMode()1809 public boolean isFullscreenMode() { 1810 synchronized (mH) { 1811 return mFullscreenMode; 1812 } 1813 } 1814 1815 /** 1816 * Return {@code true} if the given view is the currently active view for the input method. 1817 */ isActive(View view)1818 public boolean isActive(View view) { 1819 // Re-dispatch if there is a context mismatch. 1820 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 1821 if (fallbackImm != null) { 1822 return fallbackImm.isActive(view); 1823 } 1824 1825 checkFocus(); 1826 synchronized (mH) { 1827 return hasServedByInputMethodLocked(view) && mCurrentEditorInfo != null; 1828 } 1829 } 1830 1831 /** 1832 * Return {@code true} if any view is currently active for the input method. 1833 */ isActive()1834 public boolean isActive() { 1835 checkFocus(); 1836 synchronized (mH) { 1837 return getServedViewLocked() != null && mCurrentEditorInfo != null; 1838 } 1839 } 1840 1841 /** 1842 * Returns {@code true} if the given view's {@link ViewRootImpl} is the currently active one 1843 * for the {@code InputMethodManager}. 1844 * 1845 * @hide 1846 */ 1847 @TestApi 1848 @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) isCurrentRootView(@onNull View attachedView)1849 public boolean isCurrentRootView(@NonNull View attachedView) { 1850 synchronized (mH) { 1851 return mCurRootView == attachedView.getViewRootImpl(); 1852 } 1853 } 1854 1855 /** 1856 * Return {@code true} if the currently served view is accepting full text edits. 1857 * If {@code false}, it has no input connection, so it can only handle raw key events. 1858 */ isAcceptingText()1859 public boolean isAcceptingText() { 1860 checkFocus(); 1861 synchronized (mH) { 1862 return mServedInputConnection != null; 1863 } 1864 } 1865 1866 /** 1867 * Return {@code true} if the input method is suppressing system spell checker. 1868 */ isInputMethodSuppressingSpellChecker()1869 public boolean isInputMethodSuppressingSpellChecker() { 1870 synchronized (mH) { 1871 return mCurBindState != null 1872 && mCurBindState.mIsInputMethodSuppressingSpellChecker; 1873 } 1874 } 1875 1876 /** 1877 * Reset all of the state associated with being bound to an input method. 1878 */ 1879 @GuardedBy("mH") clearBindingLocked()1880 private void clearBindingLocked() { 1881 if (DEBUG) Log.v(TAG, "Clearing binding!"); 1882 clearConnectionLocked(); 1883 updateInputChannelLocked(null); 1884 mCurId = null; // for @UnsupportedAppUsage 1885 mCurMethod = null; // for @UnsupportedAppUsage 1886 // We only reset sequence number for input method, but not accessibility. 1887 mCurBindState = null; 1888 } 1889 1890 /** 1891 * Reset all of the state associated with being bound to an accessibility service. 1892 */ 1893 @GuardedBy("mH") clearAccessibilityBindingLocked(int id)1894 private void clearAccessibilityBindingLocked(int id) { 1895 if (DEBUG) Log.v(TAG, "Clearing accessibility binding " + id); 1896 mAccessibilityInputMethodSession.remove(id); 1897 } 1898 1899 /** 1900 * Reset all of the state associated with being bound to all accessibility services. 1901 */ 1902 @GuardedBy("mH") clearAllAccessibilityBindingLocked()1903 private void clearAllAccessibilityBindingLocked() { 1904 if (DEBUG) Log.v(TAG, "Clearing all accessibility bindings"); 1905 mAccessibilityInputMethodSession.clear(); 1906 } 1907 1908 @GuardedBy("mH") updateInputChannelLocked(InputChannel channel)1909 private void updateInputChannelLocked(InputChannel channel) { 1910 if (areSameInputChannel(mCurChannel, channel)) { 1911 return; 1912 } 1913 // TODO(b/238720598) : Requirements when design a new protocol for InputChannel 1914 // channel is a dupe of 'mCurChannel', because they have the same token, and represent 1915 // the same connection. Ignore the incoming channel and keep using 'mCurChannel' to 1916 // avoid confusing the InputEventReceiver. 1917 if (mCurSender != null) { 1918 flushPendingEventsLocked(); 1919 mCurSender.dispose(); 1920 mCurSender = null; 1921 } 1922 1923 if (mCurChannel != null) { 1924 mCurChannel.dispose(); 1925 } 1926 mCurChannel = channel; 1927 } 1928 areSameInputChannel(@ullable InputChannel lhs, @Nullable InputChannel rhs)1929 private static boolean areSameInputChannel(@Nullable InputChannel lhs, 1930 @Nullable InputChannel rhs) { 1931 if (lhs == rhs) { 1932 return true; 1933 } 1934 if (lhs == null || rhs == null) { 1935 return false; 1936 } 1937 return lhs.getToken() == rhs.getToken(); 1938 } 1939 1940 /** 1941 * Reset all of the state associated with a served view being connected 1942 * to an input method 1943 */ 1944 @GuardedBy("mH") clearConnectionLocked()1945 private void clearConnectionLocked() { 1946 mCurrentEditorInfo = null; 1947 mPreviousViewFocusParameters = null; 1948 if (mServedInputConnection != null) { 1949 mServedInputConnection.deactivate(); 1950 mServedInputConnection = null; 1951 mServedInputConnectionHandler = null; 1952 } 1953 } 1954 1955 /** 1956 * Disconnect any existing input connection, clearing the served view. 1957 */ 1958 @UnsupportedAppUsage 1959 @GuardedBy("mH") finishInputLocked()1960 void finishInputLocked() { 1961 mVirtualDisplayToScreenMatrix = null; 1962 View clearedView = null; 1963 mNextServedView = null; 1964 if (mServedView != null) { 1965 clearedView = mServedView; 1966 mServedView = null; 1967 } 1968 if (clearedView != null) { 1969 if (DEBUG) { 1970 Log.v(TAG, "FINISH INPUT: mServedView=" 1971 + InputMethodDebug.dumpViewInfo(clearedView)); 1972 } 1973 mCompletions = null; 1974 mServedConnecting = false; 1975 clearConnectionLocked(); 1976 } 1977 // Clear the back callbacks held by the ime dispatcher to avoid memory leaks. 1978 mImeDispatcher.clear(); 1979 } 1980 1981 /** 1982 * Clears the {@link #mCurRootView} if it's no longer window focused and the connection is 1983 * no longer active. 1984 * 1985 * @return {@code} true iff it was cleared. 1986 */ 1987 @GuardedBy("mH") clearCurRootViewIfNeeded()1988 private boolean clearCurRootViewIfNeeded() { 1989 if (!mActive && !mCurRootViewWindowFocused) { 1990 finishInputLocked(); 1991 mDelegate.setCurrentRootViewLocked(null); 1992 1993 return true; 1994 } 1995 1996 return false; 1997 } 1998 displayCompletions(View view, CompletionInfo[] completions)1999 public void displayCompletions(View view, CompletionInfo[] completions) { 2000 // Re-dispatch if there is a context mismatch. 2001 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 2002 if (fallbackImm != null) { 2003 fallbackImm.displayCompletions(view, completions); 2004 return; 2005 } 2006 2007 checkFocus(); 2008 synchronized (mH) { 2009 if (!hasServedByInputMethodLocked(view)) { 2010 return; 2011 } 2012 2013 mCompletions = completions; 2014 if (isImeSessionAvailableLocked()) { 2015 mCurBindState.mImeSession.displayCompletions(mCompletions); 2016 } 2017 } 2018 } 2019 updateExtractedText(View view, int token, ExtractedText text)2020 public void updateExtractedText(View view, int token, ExtractedText text) { 2021 // Re-dispatch if there is a context mismatch. 2022 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 2023 if (fallbackImm != null) { 2024 fallbackImm.updateExtractedText(view, token, text); 2025 return; 2026 } 2027 2028 checkFocus(); 2029 synchronized (mH) { 2030 if (!hasServedByInputMethodLocked(view)) { 2031 return; 2032 } 2033 2034 if (isImeSessionAvailableLocked()) { 2035 mCurBindState.mImeSession.updateExtractedText(token, text); 2036 } 2037 } 2038 } 2039 2040 /** @hide */ 2041 @IntDef(flag = true, prefix = { "SHOW_" }, value = { 2042 SHOW_IMPLICIT, 2043 SHOW_FORCED, 2044 }) 2045 @Retention(RetentionPolicy.SOURCE) 2046 public @interface ShowFlags {} 2047 2048 /** 2049 * Flag for {@link #showSoftInput} to indicate that this is an implicit 2050 * request to show the input window, not as the result of a direct request 2051 * by the user. The window may not be shown in this case. 2052 */ 2053 public static final int SHOW_IMPLICIT = 0x0001; 2054 2055 /** 2056 * Flag for {@link #showSoftInput} to indicate that the user has forced 2057 * the input method open (such as by long-pressing menu) so it should 2058 * not be closed until they explicitly do so. 2059 * 2060 * @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead 2061 * to the soft input remaining visible even when the calling application is closed. The 2062 * use of this flag can make the soft input remains visible globally. Starting in 2063 * {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the 2064 * caller is currently focused. 2065 */ 2066 @Deprecated 2067 public static final int SHOW_FORCED = 0x0002; 2068 2069 /** 2070 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without 2071 * a result receiver: explicitly request that the current input method's 2072 * soft input area be shown to the user, if needed. 2073 * 2074 * @param view The currently focused view, which would like to receive soft keyboard input. 2075 * Note that this view is only considered focused here if both it itself has 2076 * {@link View#isFocused view focus}, and its containing window has 2077 * {@link View#hasWindowFocus window focus}. Otherwise the call fails and 2078 * returns {@code false}. 2079 */ showSoftInput(View view, @ShowFlags int flags)2080 public boolean showSoftInput(View view, @ShowFlags int flags) { 2081 // Re-dispatch if there is a context mismatch. 2082 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 2083 if (fallbackImm != null) { 2084 return fallbackImm.showSoftInput(view, flags); 2085 } 2086 2087 return showSoftInput(view, flags, null); 2088 } 2089 2090 /** 2091 * Flag for the {@link ResultReceiver} result code from 2092 * {@link #showSoftInput(View, int, ResultReceiver)} and 2093 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 2094 * state of the soft input window was unchanged and remains shown. 2095 */ 2096 public static final int RESULT_UNCHANGED_SHOWN = 0; 2097 2098 /** 2099 * Flag for the {@link ResultReceiver} result code from 2100 * {@link #showSoftInput(View, int, ResultReceiver)} and 2101 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 2102 * state of the soft input window was unchanged and remains hidden. 2103 */ 2104 public static final int RESULT_UNCHANGED_HIDDEN = 1; 2105 2106 /** 2107 * Flag for the {@link ResultReceiver} result code from 2108 * {@link #showSoftInput(View, int, ResultReceiver)} and 2109 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 2110 * state of the soft input window changed from hidden to shown. 2111 */ 2112 public static final int RESULT_SHOWN = 2; 2113 2114 /** 2115 * Flag for the {@link ResultReceiver} result code from 2116 * {@link #showSoftInput(View, int, ResultReceiver)} and 2117 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 2118 * state of the soft input window changed from shown to hidden. 2119 */ 2120 public static final int RESULT_HIDDEN = 3; 2121 2122 /** 2123 * Explicitly request that the current input method's soft input area be 2124 * shown to the user, if needed. Call this if the user interacts with 2125 * your view in such a way that they have expressed they would like to 2126 * start performing input into it. 2127 * 2128 * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to 2129 * this method can be a long-lived object, because it may not be 2130 * garbage-collected until all the corresponding {@link ResultReceiver} 2131 * objects transferred to different processes get garbage-collected. 2132 * Follow the general patterns to avoid memory leaks in Android. 2133 * Consider to use {@link java.lang.ref.WeakReference} so that application 2134 * logic objects such as {@link android.app.Activity} and {@link Context} 2135 * can be garbage collected regardless of the lifetime of 2136 * {@link ResultReceiver}. 2137 * 2138 * @param view The currently focused view, which would like to receive soft keyboard input. 2139 * Note that this view is only considered focused here if both it itself has 2140 * {@link View#isFocused view focus}, and its containing window has 2141 * {@link View#hasWindowFocus window focus}. Otherwise the call fails and 2142 * returns {@code false}. 2143 * @param resultReceiver If non-null, this will be called by the IME when 2144 * it has processed your request to tell you what it has done. The result 2145 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 2146 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 2147 * {@link #RESULT_HIDDEN}. 2148 */ showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver)2149 public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) { 2150 return showSoftInput(view, null /* statsToken */, flags, resultReceiver, 2151 SoftInputShowHideReason.SHOW_SOFT_INPUT); 2152 } 2153 showSoftInput(View view, @Nullable ImeTracker.Token statsToken, @ShowFlags int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)2154 private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, 2155 @ShowFlags int flags, ResultReceiver resultReceiver, 2156 @SoftInputShowHideReason int reason) { 2157 if (statsToken == null) { 2158 statsToken = ImeTracker.forLogging().onRequestShow(null /* component */, 2159 Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason); 2160 } 2161 ImeTracker.forLatency().onRequestShow(statsToken, ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, 2162 reason, ActivityThread::currentApplication); 2163 ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this, 2164 null /* icProto */); 2165 // Re-dispatch if there is a context mismatch. 2166 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 2167 if (fallbackImm != null) { 2168 return fallbackImm.showSoftInput(view, statsToken, flags, resultReceiver, reason); 2169 } 2170 2171 checkFocus(); 2172 synchronized (mH) { 2173 if (!hasServedByInputMethodLocked(view)) { 2174 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 2175 ImeTracker.forLatency().onShowFailed( 2176 statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED, 2177 ActivityThread::currentApplication); 2178 Log.w(TAG, "Ignoring showSoftInput() as view=" + view + " is not served."); 2179 return false; 2180 } 2181 2182 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 2183 2184 // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread. 2185 // TODO(b/229426865): call WindowInsetsController#show instead. 2186 mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED)); 2187 Log.d(TAG, "showSoftInput() view=" + view + " flags=" + flags + " reason=" 2188 + InputMethodDebug.softInputDisplayReasonToString(reason)); 2189 return IInputMethodManagerGlobalInvoker.showSoftInput( 2190 mClient, 2191 view.getWindowToken(), 2192 statsToken, 2193 flags, 2194 mCurRootView.getLastClickToolType(), 2195 resultReceiver, 2196 reason); 2197 } 2198 } 2199 2200 /** 2201 * This method is still kept for a while until androidx.appcompat.widget.SearchView ver. 26.0 2202 * is publicly released because previous implementations of that class had relied on this method 2203 * via reflection. 2204 * 2205 * @deprecated This is a hidden API. You should never use this. 2206 * @hide 2207 */ 2208 @Deprecated 2209 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499) showSoftInputUnchecked(@howFlags int flags, ResultReceiver resultReceiver)2210 public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) { 2211 synchronized (mH) { 2212 final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow( 2213 null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, 2214 SoftInputShowHideReason.SHOW_SOFT_INPUT); 2215 2216 Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be" 2217 + " removed soon. If you are using androidx.appcompat.widget.SearchView," 2218 + " please update to version 26.0 or newer version."); 2219 if (mCurRootView == null || mCurRootView.getView() == null) { 2220 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 2221 Log.w(TAG, "No current root view, ignoring showSoftInputUnchecked()"); 2222 return; 2223 } 2224 2225 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 2226 2227 // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread. 2228 // TODO(b/229426865): call WindowInsetsController#show instead. 2229 mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED)); 2230 IInputMethodManagerGlobalInvoker.showSoftInput( 2231 mClient, 2232 mCurRootView.getView().getWindowToken(), 2233 statsToken, 2234 flags, 2235 mCurRootView.getLastClickToolType(), 2236 resultReceiver, 2237 SoftInputShowHideReason.SHOW_SOFT_INPUT); 2238 } 2239 } 2240 2241 /** @hide */ 2242 @IntDef(flag = true, prefix = { "HIDE_" }, value = { 2243 HIDE_IMPLICIT_ONLY, 2244 HIDE_NOT_ALWAYS, 2245 }) 2246 @Retention(RetentionPolicy.SOURCE) 2247 public @interface HideFlags {} 2248 2249 /** 2250 * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestHideSelf(int)} 2251 * to indicate that the soft input window should only be hidden if it was not explicitly shown 2252 * by the user. 2253 */ 2254 public static final int HIDE_IMPLICIT_ONLY = 0x0001; 2255 2256 /** 2257 * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestShowSelf(int)} 2258 * to indicate that the soft input window should normally be hidden, unless it was originally 2259 * shown with {@link #SHOW_FORCED}. 2260 */ 2261 public static final int HIDE_NOT_ALWAYS = 0x0002; 2262 2263 /** 2264 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} 2265 * without a result: request to hide the soft input window from the 2266 * context of the window that is currently accepting input. 2267 * 2268 * @param windowToken The token of the window that is making the request, 2269 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 2270 */ hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags)2271 public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) { 2272 return hideSoftInputFromWindow(windowToken, flags, null); 2273 } 2274 2275 /** 2276 * Request to hide the soft input window from the context of the window 2277 * that is currently accepting input. This should be called as a result 2278 * of the user doing some actually than fairly explicitly requests to 2279 * have the input window hidden. 2280 * 2281 * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to 2282 * this method can be a long-lived object, because it may not be 2283 * garbage-collected until all the corresponding {@link ResultReceiver} 2284 * objects transferred to different processes get garbage-collected. 2285 * Follow the general patterns to avoid memory leaks in Android. 2286 * Consider to use {@link java.lang.ref.WeakReference} so that application 2287 * logic objects such as {@link android.app.Activity} and {@link Context} 2288 * can be garbage collected regardless of the lifetime of 2289 * {@link ResultReceiver}. 2290 * 2291 * @param windowToken The token of the window that is making the request, 2292 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 2293 * @param resultReceiver If non-null, this will be called by the IME when 2294 * it has processed your request to tell you what it has done. The result 2295 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 2296 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 2297 * {@link #RESULT_HIDDEN}. 2298 */ hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, ResultReceiver resultReceiver)2299 public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, 2300 ResultReceiver resultReceiver) { 2301 return hideSoftInputFromWindow(windowToken, flags, resultReceiver, 2302 SoftInputShowHideReason.HIDE_SOFT_INPUT); 2303 } 2304 hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)2305 private boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, 2306 ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { 2307 final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide( 2308 null /* component */, Process.myUid(), 2309 ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, reason); 2310 ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, 2311 reason, ActivityThread::currentApplication); 2312 ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow", 2313 this, null /* icProto */); 2314 checkFocus(); 2315 synchronized (mH) { 2316 final View servedView = getServedViewLocked(); 2317 if (servedView == null || servedView.getWindowToken() != windowToken) { 2318 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 2319 ImeTracker.forLatency().onHideFailed(statsToken, 2320 ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication); 2321 return false; 2322 } 2323 2324 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 2325 2326 return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken, 2327 flags, resultReceiver, reason); 2328 } 2329 } 2330 2331 /** 2332 * Start stylus handwriting session. 2333 * 2334 * If supported by the current input method, a stylus handwriting session is started on the 2335 * given View, capturing all stylus input and converting it to InputConnection commands. 2336 * 2337 * If handwriting mode is started successfully by the IME, any currently dispatched stylus 2338 * pointers will be {@code android.view.MotionEvent#FLAG_CANCELED} cancelled. 2339 * 2340 * If Stylus handwriting mode is not supported or cannot be fulfilled for any reason by IME, 2341 * request will be ignored and Stylus touch will continue as normal touch input. Ideally, 2342 * {@link #isStylusHandwritingAvailable()} should be called first to determine if stylus 2343 * handwriting is supported by IME. 2344 * 2345 * @param view the View for which stylus handwriting is requested. It and 2346 * {@link View#hasWindowFocus its window} must be {@link View#hasFocus focused}. 2347 * @see #isStylusHandwritingAvailable() 2348 */ startStylusHandwriting(@onNull View view)2349 public void startStylusHandwriting(@NonNull View view) { 2350 startStylusHandwritingInternal(view, null /* delegatorPackageName */); 2351 } 2352 startStylusHandwritingInternal( @onNull View view, @Nullable String delegatorPackageName)2353 private boolean startStylusHandwritingInternal( 2354 @NonNull View view, @Nullable String delegatorPackageName) { 2355 Objects.requireNonNull(view); 2356 2357 // Re-dispatch if there is a context mismatch. 2358 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 2359 if (fallbackImm != null) { 2360 fallbackImm.startStylusHandwritingInternal(view, delegatorPackageName); 2361 } 2362 2363 boolean useDelegation = !TextUtils.isEmpty(delegatorPackageName); 2364 2365 checkFocus(); 2366 synchronized (mH) { 2367 if (!hasServedByInputMethodLocked(view)) { 2368 Log.w(TAG, 2369 "Ignoring startStylusHandwriting as view=" + view + " is not served."); 2370 return false; 2371 } 2372 if (view.getViewRootImpl() != mCurRootView) { 2373 Log.w(TAG, 2374 "Ignoring startStylusHandwriting: View's window does not have focus."); 2375 return false; 2376 } 2377 if (useDelegation) { 2378 return IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegation( 2379 mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(), 2380 delegatorPackageName); 2381 } else { 2382 IInputMethodManagerGlobalInvoker.startStylusHandwriting(mClient); 2383 } 2384 return false; 2385 } 2386 } 2387 2388 /** 2389 * Prepares delegation of starting stylus handwriting session to a different editor in same 2390 * or different window than the view on which initial handwriting stroke was detected. 2391 * 2392 * Delegation can be used to start stylus handwriting session before the {@link Editor} view or 2393 * its {@link InputConnection} is started. Calling this method starts buffering of stylus 2394 * motion events until {@link #acceptStylusHandwritingDelegation(View)} is called, at which 2395 * point the handwriting session can be started and the buffered stylus motion events will be 2396 * delivered to the IME. 2397 * e.g. Delegation can be used when initial handwriting stroke is 2398 * on a pseudo {@link Editor} like widget (with no {@link InputConnection}) but actual 2399 * {@link Editor} is on a different window. 2400 * 2401 * <p> Note: If an actual {@link Editor} capable of {@link InputConnection} is being scribbled 2402 * upon using stylus, use {@link #startStylusHandwriting(View)} instead.</p> 2403 * 2404 * @param delegatorView the view that receives initial stylus stroke and delegates it to the 2405 * actual editor. Its window must {@link View#hasWindowFocus have focus}. 2406 * @see #prepareStylusHandwritingDelegation(View, String) 2407 * @see #acceptStylusHandwritingDelegation(View) 2408 * @see #startStylusHandwriting(View) 2409 */ prepareStylusHandwritingDelegation(@onNull View delegatorView)2410 public void prepareStylusHandwritingDelegation(@NonNull View delegatorView) { 2411 prepareStylusHandwritingDelegation( 2412 delegatorView, delegatorView.getContext().getOpPackageName()); 2413 } 2414 2415 /** 2416 * Prepares delegation of starting stylus handwriting session to a different editor in same or a 2417 * different window in a different package than the view on which initial handwriting stroke 2418 * was detected. 2419 * 2420 * Delegation can be used to start stylus handwriting session before the {@link Editor} view or 2421 * its {@link InputConnection} is started. Calling this method starts buffering of stylus 2422 * motion events until {@link #acceptStylusHandwritingDelegation(View, String)} is called, at 2423 * which point the handwriting session can be started and the buffered stylus motion events will 2424 * be delivered to the IME. 2425 * e.g. Delegation can be used when initial handwriting stroke is 2426 * on a pseudo {@link Editor} like widget (with no {@link InputConnection}) but actual 2427 * {@link Editor} is on a different window in the given package. 2428 * 2429 * <p>Note: If delegator and delegate are in same package use 2430 * {@link #prepareStylusHandwritingDelegation(View)} instead.</p> 2431 * 2432 * @param delegatorView the view that receives initial stylus stroke and delegates it to the 2433 * actual editor. Its window must {@link View#hasWindowFocus have focus}. 2434 * @param delegatePackageName package name that contains actual {@link Editor} which should 2435 * start stylus handwriting session by calling {@link #acceptStylusHandwritingDelegation}. 2436 * @see #prepareStylusHandwritingDelegation(View) 2437 * @see #acceptStylusHandwritingDelegation(View, String) 2438 */ prepareStylusHandwritingDelegation( @onNull View delegatorView, @NonNull String delegatePackageName)2439 public void prepareStylusHandwritingDelegation( 2440 @NonNull View delegatorView, @NonNull String delegatePackageName) { 2441 Objects.requireNonNull(delegatorView); 2442 Objects.requireNonNull(delegatePackageName); 2443 2444 // Re-dispatch if there is a context mismatch. 2445 final InputMethodManager fallbackImm = 2446 getFallbackInputMethodManagerIfNecessary(delegatorView); 2447 if (fallbackImm != null) { 2448 fallbackImm.prepareStylusHandwritingDelegation(delegatorView, delegatePackageName); 2449 } 2450 2451 IInputMethodManagerGlobalInvoker.prepareStylusHandwritingDelegation( 2452 mClient, 2453 UserHandle.myUserId(), 2454 delegatePackageName, 2455 delegatorView.getContext().getOpPackageName()); 2456 } 2457 2458 /** 2459 * Accepts and starts a stylus handwriting session on the delegate view, if handwriting 2460 * initiation delegation was previously requested using 2461 * {@link #prepareStylusHandwritingDelegation(View)} from the delegator. 2462 * 2463 * <p>Note: If delegator and delegate are in different application packages, use 2464 * {@link #acceptStylusHandwritingDelegation(View, String)} instead.</p> 2465 * 2466 * @param delegateView delegate view capable of receiving input via {@link InputConnection} 2467 * on which {@link #startStylusHandwriting(View)} will be called. 2468 * @return {@code true} if view belongs to same application package as used in 2469 * {@link #prepareStylusHandwritingDelegation(View)} and handwriting session can start. 2470 * @see #acceptStylusHandwritingDelegation(View, String) 2471 * @see #prepareStylusHandwritingDelegation(View) 2472 */ acceptStylusHandwritingDelegation(@onNull View delegateView)2473 public boolean acceptStylusHandwritingDelegation(@NonNull View delegateView) { 2474 return startStylusHandwritingInternal( 2475 delegateView, delegateView.getContext().getOpPackageName()); 2476 } 2477 2478 /** 2479 * Accepts and starts a stylus handwriting session on the delegate view, if handwriting 2480 * initiation delegation was previously requested using 2481 * {@link #prepareStylusHandwritingDelegation(View, String)} from te delegator and the view 2482 * belongs to a specified delegate package. 2483 * 2484 * <p>Note: If delegator and delegate are in same application package use 2485 * {@link #acceptStylusHandwritingDelegation(View)} instead.</p> 2486 * 2487 * @param delegateView delegate view capable of receiving input via {@link InputConnection} 2488 * on which {@link #startStylusHandwriting(View)} will be called. 2489 * @param delegatorPackageName package name of the delegator that handled initial stylus stroke. 2490 * @return {@code true} if view belongs to allowed delegate package declared in 2491 * {@link #prepareStylusHandwritingDelegation(View, String)} and handwriting session can start. 2492 * @see #prepareStylusHandwritingDelegation(View, String) 2493 * @see #acceptStylusHandwritingDelegation(View) 2494 */ acceptStylusHandwritingDelegation( @onNull View delegateView, @NonNull String delegatorPackageName)2495 public boolean acceptStylusHandwritingDelegation( 2496 @NonNull View delegateView, @NonNull String delegatorPackageName) { 2497 Objects.requireNonNull(delegatorPackageName); 2498 2499 return startStylusHandwritingInternal(delegateView, delegatorPackageName); 2500 } 2501 2502 /** 2503 * This method toggles the input method window display. 2504 * If the input window is already displayed, it gets hidden. 2505 * If not the input window will be displayed. 2506 * @param windowToken The token of the window that is making the request, 2507 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 2508 * 2509 * @deprecated Use {@link #showSoftInput(View, int)} or 2510 * {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead. 2511 * In particular during focus changes, the current visibility of the IME is not 2512 * well defined. Starting in {@link Build.VERSION_CODES#S Android S}, this only 2513 * has an effect if the calling app is the current IME focus. 2514 */ 2515 @Deprecated toggleSoftInputFromWindow(IBinder windowToken, @ShowFlags int showFlags, @HideFlags int hideFlags)2516 public void toggleSoftInputFromWindow(IBinder windowToken, @ShowFlags int showFlags, 2517 @HideFlags int hideFlags) { 2518 ImeTracing.getInstance().triggerClientDump( 2519 "InputMethodManager#toggleSoftInputFromWindow", InputMethodManager.this, 2520 null /* icProto */); 2521 synchronized (mH) { 2522 final View servedView = getServedViewLocked(); 2523 if (servedView == null || servedView.getWindowToken() != windowToken) { 2524 return; 2525 } 2526 toggleSoftInput(showFlags, hideFlags); 2527 } 2528 } 2529 2530 /** 2531 * This method toggles the input method window display. 2532 * 2533 * If the input window is already displayed, it gets hidden. 2534 * If not the input window will be displayed. 2535 * 2536 * @deprecated Use {@link #showSoftInput(View, int)} or 2537 * {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead. 2538 * In particular during focus changes, the current visibility of the IME is not 2539 * well defined. Starting in {@link Build.VERSION_CODES#S Android S}, this only 2540 * has an effect if the calling app is the current IME focus. 2541 */ 2542 @Deprecated toggleSoftInput(@howFlags int showFlags, @HideFlags int hideFlags)2543 public void toggleSoftInput(@ShowFlags int showFlags, @HideFlags int hideFlags) { 2544 ImeTracing.getInstance().triggerClientDump( 2545 "InputMethodManager#toggleSoftInput", InputMethodManager.this, 2546 null /* icProto */); 2547 synchronized (mH) { 2548 final View view = getServedViewLocked(); 2549 if (view != null) { 2550 final WindowInsets rootInsets = view.getRootWindowInsets(); 2551 if (rootInsets != null && rootInsets.isVisible(WindowInsets.Type.ime())) { 2552 hideSoftInputFromWindow(view.getWindowToken(), hideFlags, null, 2553 SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT); 2554 } else { 2555 showSoftInput(view, null /* statsToken */, showFlags, null /* resultReceiver */, 2556 SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT); 2557 } 2558 } 2559 } 2560 } 2561 2562 /** 2563 * If the input method is currently connected to the given view, 2564 * restart it with its new contents. You should call this when the text 2565 * within your view changes outside of the normal input method or key 2566 * input flow, such as when an application calls TextView.setText(). 2567 * 2568 * @param view The view whose text has changed. 2569 */ restartInput(View view)2570 public void restartInput(View view) { 2571 // Re-dispatch if there is a context mismatch. 2572 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 2573 if (fallbackImm != null) { 2574 fallbackImm.restartInput(view); 2575 return; 2576 } 2577 2578 checkFocus(); 2579 synchronized (mH) { 2580 if (!hasServedByInputMethodLocked(view)) { 2581 return; 2582 } 2583 2584 mServedConnecting = true; 2585 } 2586 2587 startInputInner(StartInputReason.APP_CALLED_RESTART_INPUT_API, null, 0, 0, 0); 2588 } 2589 2590 /** 2591 * Sends an async signal to the IME to reset the currently served {@link InputConnection}. 2592 * 2593 * @param inputConnection the connection to be invalidated. 2594 * @param textSnapshot {@link TextSnapshot} to be used to update {@link EditorInfo}. 2595 * @param sessionId the session ID to be sent. 2596 * @return {@code true} if the operation is done. {@code false} if the caller needs to fall back 2597 * to {@link InputMethodManager#restartInput(View)}. 2598 * @hide 2599 */ doInvalidateInput(@onNull RemoteInputConnectionImpl inputConnection, @NonNull TextSnapshot textSnapshot, int sessionId)2600 public boolean doInvalidateInput(@NonNull RemoteInputConnectionImpl inputConnection, 2601 @NonNull TextSnapshot textSnapshot, int sessionId) { 2602 synchronized (mH) { 2603 if (mServedInputConnection != inputConnection || mCurrentEditorInfo == null) { 2604 // OK to ignore because the calling InputConnection is already abandoned. 2605 return true; 2606 } 2607 if (!isImeSessionAvailableLocked()) { 2608 // IME is not yet bound to the client. Need to fall back to the restartInput(). 2609 return false; 2610 } 2611 final EditorInfo editorInfo = mCurrentEditorInfo.createCopyInternal(); 2612 editorInfo.initialSelStart = mCursorSelStart = textSnapshot.getSelectionStart(); 2613 editorInfo.initialSelEnd = mCursorSelEnd = textSnapshot.getSelectionEnd(); 2614 mCursorCandStart = textSnapshot.getCompositionStart(); 2615 mCursorCandEnd = textSnapshot.getCompositionEnd(); 2616 editorInfo.initialCapsMode = textSnapshot.getCursorCapsMode(); 2617 editorInfo.setInitialSurroundingTextInternal(textSnapshot.getSurroundingText()); 2618 mCurBindState.mImeSession.invalidateInput(editorInfo, mServedInputConnection, 2619 sessionId); 2620 final IRemoteAccessibilityInputConnection accessibilityInputConnection = 2621 mServedInputConnection.asIRemoteAccessibilityInputConnection(); 2622 forAccessibilitySessionsLocked(wrapper -> wrapper.invalidateInput(editorInfo, 2623 accessibilityInputConnection, sessionId)); 2624 return true; 2625 } 2626 } 2627 2628 /** 2629 * Gives a hint to the system that the text associated with {@code view} is updated by something 2630 * that is not an input method editor (IME), so that the system can cancel any pending text 2631 * editing requests from the IME until it receives the new editing context such as surrounding 2632 * text provided by {@link InputConnection#takeSnapshot()}. 2633 * 2634 * <p>When {@code view} does not support {@link InputConnection#takeSnapshot()} protocol, 2635 * calling this method may trigger {@link View#onCreateInputConnection(EditorInfo)}.</p> 2636 * 2637 * <p>Unlike {@link #restartInput(View)}, this API does not immediately interact with 2638 * {@link InputConnection}. Instead, the application may later receive 2639 * {@link InputConnection#takeSnapshot()} as needed so that the system can capture new editing 2640 * context for the IME. For instance, successive invocations of this API can be coerced into a 2641 * single (or zero) callback of {@link InputConnection#takeSnapshot()}.</p> 2642 * 2643 * @param view The view whose text has changed. 2644 * @see #restartInput(View) 2645 */ invalidateInput(@onNull View view)2646 public void invalidateInput(@NonNull View view) { 2647 Objects.requireNonNull(view); 2648 2649 // Re-dispatch if there is a context mismatch. 2650 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 2651 if (fallbackImm != null) { 2652 fallbackImm.invalidateInput(view); 2653 return; 2654 } 2655 2656 synchronized (mH) { 2657 if (mServedInputConnection == null || getServedViewLocked() != view) { 2658 return; 2659 } 2660 mServedInputConnection.scheduleInvalidateInput(); 2661 } 2662 } 2663 2664 /** 2665 * Starts an input connection from the served view that gains the window focus. 2666 * Note that this method should *NOT* be called inside of {@code mH} lock to prevent start input 2667 * background thread may blocked by other methods which already inside {@code mH} lock. 2668 * 2669 * <p>{@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required when and only when 2670 * {@code userId} is different from the user id of the current process.</p> 2671 */ 2672 @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) startInputInner(@tartInputReason int startInputReason, @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags)2673 private boolean startInputInner(@StartInputReason int startInputReason, 2674 @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags, 2675 @SoftInputModeFlags int softInputMode, int windowFlags) { 2676 final View view; 2677 synchronized (mH) { 2678 view = getServedViewLocked(); 2679 2680 // Make sure we have a window token for the served view. 2681 if (DEBUG) { 2682 Log.v(TAG, "Starting input: view=" + InputMethodDebug.dumpViewInfo(view) + 2683 " reason=" + InputMethodDebug.startInputReasonToString(startInputReason)); 2684 } 2685 if (view == null) { 2686 if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); 2687 return false; 2688 } 2689 } 2690 2691 // Now we need to get an input connection from the served view. 2692 // This is complicated in a couple ways: we can't be holding our lock 2693 // when calling out to the view, and we need to make sure we call into 2694 // the view on the same thread that is driving its view hierarchy. 2695 Handler vh = view.getHandler(); 2696 if (vh == null) { 2697 // If the view doesn't have a handler, something has changed out 2698 // from under us, so just close the current input. 2699 // If we don't close the current input, the current input method can remain on the 2700 // screen without a connection. 2701 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view! Close current input."); 2702 closeCurrentInput(); 2703 return false; 2704 } 2705 if (vh.getLooper() != Looper.myLooper()) { 2706 // The view is running on a different thread than our own, so 2707 // we need to reschedule our work for over there. 2708 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); 2709 vh.post(() -> startInputOnWindowFocusGainInternal(startInputReason, null, 0, 0, 0)); 2710 return false; 2711 } 2712 2713 if (windowGainingFocus == null) { 2714 windowGainingFocus = view.getWindowToken(); 2715 if (windowGainingFocus == null) { 2716 Log.e(TAG, "ABORT input: ServedView must be attached to a Window"); 2717 return false; 2718 } 2719 startInputFlags = getStartInputFlags(view, startInputFlags); 2720 softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode; 2721 windowFlags = view.getViewRootImpl().mWindowAttributes.flags; 2722 } 2723 2724 // Okay we are now ready to call into the served view and have it 2725 // do its stuff. 2726 // Life is good: let's hook everything up! 2727 final Pair<InputConnection, EditorInfo> connectionPair = createInputConnection(view); 2728 final InputConnection ic = connectionPair.first; 2729 final EditorInfo editorInfo = connectionPair.second; 2730 final Handler icHandler; 2731 InputBindResult res = null; 2732 final boolean hasServedView; 2733 synchronized (mH) { 2734 // Now that we are locked again, validate that our state hasn't 2735 // changed. 2736 final View servedView = getServedViewLocked(); 2737 if (servedView != view || !mServedConnecting) { 2738 // Something else happened, so abort. 2739 if (DEBUG) Log.v(TAG, "Starting input: finished by someone else." 2740 + " view=" + InputMethodDebug.dumpViewInfo(view) 2741 + " servedView=" + InputMethodDebug.dumpViewInfo(servedView) 2742 + " mServedConnecting=" + mServedConnecting); 2743 if (mServedInputConnection != null && startInputReason == BOUND_TO_IMMS) { 2744 // This is not an error. Once IME binds (MSG_BIND), InputConnection is fully 2745 // established. So we report this to interested recipients. 2746 reportInputConnectionOpened( 2747 mServedInputConnection.getInputConnection(), mCurrentEditorInfo, 2748 mServedInputConnectionHandler, view); 2749 } 2750 return false; 2751 } 2752 2753 // If we already have a text box, then this view is already 2754 // connected so we want to restart it. 2755 if (mCurrentEditorInfo == null) { 2756 startInputFlags |= StartInputFlags.INITIAL_CONNECTION; 2757 } 2758 2759 editorInfo.setInitialToolType(mCurRootView.getLastClickToolType()); 2760 2761 // Hook 'em up and let 'er rip. 2762 mCurrentEditorInfo = editorInfo.createCopyInternal(); 2763 // Store the previously served connection so that we can determine whether it is safe 2764 // to skip the call to startInputOrWindowGainedFocus in the IMMS 2765 final RemoteInputConnectionImpl previouslyServedConnection = mServedInputConnection; 2766 2767 mServedConnecting = false; 2768 if (mServedInputConnection != null) { 2769 mServedInputConnection.deactivate(); 2770 mServedInputConnection = null; 2771 mServedInputConnectionHandler = null; 2772 } 2773 final RemoteInputConnectionImpl servedInputConnection; 2774 if (ic != null) { 2775 mCursorSelStart = editorInfo.initialSelStart; 2776 mCursorSelEnd = editorInfo.initialSelEnd; 2777 mInitialSelStart = mCursorSelStart; 2778 mInitialSelEnd = mCursorSelEnd; 2779 mCursorCandStart = -1; 2780 mCursorCandEnd = -1; 2781 mCursorRect.setEmpty(); 2782 mCursorAnchorInfo = null; 2783 Handler handler = null; 2784 try { 2785 handler = ic.getHandler(); 2786 } catch (AbstractMethodError ignored) { 2787 // TODO(b/199934664): See if we can remove this by providing a default impl. 2788 } 2789 icHandler = handler; 2790 mServedInputConnectionHandler = icHandler; 2791 servedInputConnection = new RemoteInputConnectionImpl( 2792 icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view); 2793 } else { 2794 servedInputConnection = null; 2795 icHandler = null; 2796 mServedInputConnectionHandler = null; 2797 } 2798 mServedInputConnection = servedInputConnection; 2799 2800 if (DEBUG) { 2801 Log.v(TAG, "START INPUT: view=" + InputMethodDebug.dumpViewInfo(view) 2802 + " ic=" + ic + " editorInfo=" + editorInfo + " startInputFlags=" 2803 + InputMethodDebug.startInputFlagsToString(startInputFlags)); 2804 } 2805 2806 // When we switch between non-editable views, do not call into the IMMS. 2807 final boolean canSkip = OPTIMIZE_NONEDITABLE_VIEWS 2808 && previouslyServedConnection == null 2809 && ic == null 2810 && isSwitchingBetweenEquivalentNonEditableViews( 2811 mPreviousViewFocusParameters, startInputFlags, 2812 startInputReason, softInputMode, windowFlags); 2813 mPreviousViewFocusParameters = new ViewFocusParameterInfo(mCurrentEditorInfo, 2814 startInputFlags, startInputReason, softInputMode, windowFlags); 2815 if (canSkip) { 2816 if (DEBUG) { 2817 Log.d(TAG, "Not calling IMMS due to switching between non-editable views."); 2818 } 2819 return false; 2820 } 2821 final int targetUserId = editorInfo.targetInputMethodUser != null 2822 ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId(); 2823 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus"); 2824 res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus( 2825 startInputReason, mClient, windowGainingFocus, startInputFlags, 2826 softInputMode, windowFlags, editorInfo, servedInputConnection, 2827 servedInputConnection == null ? null 2828 : servedInputConnection.asIRemoteAccessibilityInputConnection(), 2829 view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, 2830 mImeDispatcher); 2831 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 2832 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); 2833 if (res == null) { 2834 Log.wtf(TAG, "startInputOrWindowGainedFocus must not return" 2835 + " null. startInputReason=" 2836 + InputMethodDebug.startInputReasonToString(startInputReason) 2837 + " editorInfo=" + editorInfo 2838 + " startInputFlags=" 2839 + InputMethodDebug.startInputFlagsToString(startInputFlags)); 2840 return false; 2841 } 2842 mVirtualDisplayToScreenMatrix = res.getVirtualDisplayToScreenMatrix(); 2843 if (res.id != null) { 2844 updateInputChannelLocked(res.channel); 2845 mCurMethod = res.method; // for @UnsupportedAppUsage 2846 mCurBindState = new BindState(res); 2847 mAccessibilityInputMethodSession.clear(); 2848 if (res.accessibilitySessions != null) { 2849 for (int i = 0; i < res.accessibilitySessions.size(); i++) { 2850 IAccessibilityInputMethodSessionInvoker wrapper = 2851 IAccessibilityInputMethodSessionInvoker.createOrNull( 2852 res.accessibilitySessions.valueAt(i)); 2853 if (wrapper != null) { 2854 mAccessibilityInputMethodSession.append( 2855 res.accessibilitySessions.keyAt(i), wrapper); 2856 } 2857 } 2858 } 2859 mCurId = res.id; // for @UnsupportedAppUsage 2860 } else if (res.channel != null && res.channel != mCurChannel) { 2861 res.channel.dispose(); 2862 } 2863 switch (res.result) { 2864 case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW: 2865 mRestartOnNextWindowFocus = true; 2866 mServedView = null; 2867 break; 2868 } 2869 if (mCompletions != null) { 2870 if (isImeSessionAvailableLocked()) { 2871 mCurBindState.mImeSession.displayCompletions(mCompletions); 2872 } 2873 } 2874 hasServedView = mServedView != null; 2875 } 2876 2877 // Notify the app that the InputConnection is initialized and ready for use. 2878 if (ic != null && res != null && res.method != null && hasServedView) { 2879 if (DEBUG) { 2880 Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view 2881 + ", ic=" + ic + ", editorInfo=" + editorInfo + ", handler=" + icHandler); 2882 } 2883 reportInputConnectionOpened(ic, editorInfo, icHandler, view); 2884 } 2885 2886 return true; 2887 } 2888 2889 /** 2890 * @return {@code true} when we are switching focus between two non-editable views 2891 * so that we can avoid calling {@link IInputMethodManager#startInputOrWindowGainedFocus}. 2892 */ 2893 @GuardedBy("mH") isSwitchingBetweenEquivalentNonEditableViews( @ullable ViewFocusParameterInfo previousViewFocusParameters, @StartInputFlags int startInputFlags, @StartInputReason int startInputReason, @SoftInputModeFlags int softInputMode, int windowFlags)2894 private boolean isSwitchingBetweenEquivalentNonEditableViews( 2895 @Nullable ViewFocusParameterInfo previousViewFocusParameters, 2896 @StartInputFlags int startInputFlags, 2897 @StartInputReason int startInputReason, 2898 @SoftInputModeFlags int softInputMode, 2899 int windowFlags) { 2900 return (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) == 0 2901 && (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) == 0 2902 && previousViewFocusParameters != null 2903 && previousViewFocusParameters.sameAs(mCurrentEditorInfo, 2904 startInputFlags, startInputReason, softInputMode, windowFlags); 2905 } 2906 reportInputConnectionOpened( InputConnection ic, EditorInfo editorInfo, Handler icHandler, View view)2907 private void reportInputConnectionOpened( 2908 InputConnection ic, EditorInfo editorInfo, Handler icHandler, View view) { 2909 view.onInputConnectionOpenedInternal(ic, editorInfo, icHandler); 2910 final ViewRootImpl viewRoot = view.getViewRootImpl(); 2911 if (viewRoot != null) { 2912 viewRoot.getHandwritingInitiator().onInputConnectionCreated(view); 2913 } 2914 } 2915 2916 /** 2917 * 2918 * @hide 2919 */ 2920 @TestApi 2921 @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) addVirtualStylusIdForTestSession()2922 public void addVirtualStylusIdForTestSession() { 2923 synchronized (mH) { 2924 IInputMethodManagerGlobalInvoker.addVirtualStylusIdForTestSession(mClient); 2925 } 2926 } 2927 2928 /** 2929 * Set a stylus idle-timeout after which handwriting {@code InkWindow} will be removed. 2930 * <p> This API is for tests only.</p> 2931 * @param timeout to set in milliseconds. To reset to default, use a value <= zero. 2932 * @hide 2933 */ 2934 @TestApi 2935 @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) setStylusWindowIdleTimeoutForTest(@urationMillisLong long timeout)2936 public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) { 2937 synchronized (mH) { 2938 IInputMethodManagerGlobalInvoker.setStylusWindowIdleTimeoutForTest(mClient, timeout); 2939 } 2940 } 2941 2942 /** 2943 * An empty method only to avoid crashes of apps that call this method via reflection and do not 2944 * handle {@link NoSuchMethodException} in a graceful manner. 2945 * 2946 * @deprecated This is an empty method. No framework method must call this method. 2947 * @hide 2948 */ 2949 @Deprecated 2950 @UnsupportedAppUsage(trackingBug = 37122102, maxTargetSdk = Build.VERSION_CODES.Q, 2951 publicAlternatives = "{@code androidx.activity.ComponentActivity}") windowDismissed(IBinder appWindowToken)2952 public void windowDismissed(IBinder appWindowToken) { 2953 // Intentionally empty. 2954 // 2955 // It seems that some applications call this method via reflection to null clear the 2956 // following fields that used to exist in InputMethodManager: 2957 // * InputMethodManager#mCurRootView 2958 // * InputMethodManager#mServedView 2959 // * InputMethodManager#mNextServedView 2960 // so that these objects can be garbage-collected when an Activity gets dismissed. 2961 // 2962 // It is indeed true that older versions of InputMethodManager had issues that prevented 2963 // these fields from being null-cleared when it should have been, but the understanding of 2964 // the engineering team is that all known issues have already been fixed as of Android 10. 2965 // 2966 // For older devices, developers can work around the object leaks by using 2967 // androidx.activity.ComponentActivity. 2968 // See https://issuetracker.google.com/u/1/issues/37122102 for details. 2969 // 2970 // If you believe InputMethodManager is leaking objects in API 24 or any later version, 2971 // please file a bug at https://issuetracker.google.com/issues/new?component=192705. 2972 } 2973 getStartInputFlags(View focusedView, int startInputFlags)2974 private int getStartInputFlags(View focusedView, int startInputFlags) { 2975 startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS; 2976 if (focusedView.onCheckIsTextEditor()) { 2977 startInputFlags |= StartInputFlags.IS_TEXT_EDITOR; 2978 } 2979 return startInputFlags; 2980 } 2981 2982 /** 2983 * Note that this method should *NOT* be called inside of {@code mH} lock to prevent start input 2984 * background thread may blocked by other methods which already inside {@code mH} lock. 2985 * @hide 2986 */ 2987 @UnsupportedAppUsage checkFocus()2988 public void checkFocus() { 2989 synchronized (mH) { 2990 if (mCurRootView == null) { 2991 return; 2992 } 2993 if (!checkFocusInternalLocked(false /* forceNewFocus */, mCurRootView)) { 2994 return; 2995 } 2996 } 2997 startInputOnWindowFocusGainInternal(StartInputReason.CHECK_FOCUS, 2998 null /* focusedView */, 2999 0 /* startInputFlags */, 0 /* softInputMode */, 0 /* windowFlags */); 3000 } 3001 3002 /** 3003 * Check the next served view if needs to start input. 3004 */ 3005 @GuardedBy("mH") checkFocusInternalLocked(boolean forceNewFocus, ViewRootImpl viewRootImpl)3006 private boolean checkFocusInternalLocked(boolean forceNewFocus, ViewRootImpl viewRootImpl) { 3007 if (mCurRootView != viewRootImpl) { 3008 return false; 3009 } 3010 if (mServedView == mNextServedView && !forceNewFocus) { 3011 return false; 3012 } 3013 if (DEBUG) { 3014 Log.v(TAG, "checkFocus: view=" + mServedView 3015 + " next=" + mNextServedView 3016 + " force=" + forceNewFocus 3017 + " package=" 3018 + (mServedView != null ? mServedView.getContext().getPackageName() 3019 : "<none>")); 3020 } 3021 // Close the connection when no next served view coming. 3022 if (mNextServedView == null) { 3023 finishInputLocked(); 3024 closeCurrentInput(); 3025 return false; 3026 } 3027 mServedView = mNextServedView; 3028 if (mServedInputConnection != null) { 3029 mServedInputConnection.finishComposingTextFromImm(); 3030 } 3031 return true; 3032 } 3033 3034 @UiThread onViewFocusChangedInternal(@ullable View view, boolean hasFocus)3035 private void onViewFocusChangedInternal(@Nullable View view, boolean hasFocus) { 3036 if (view == null || view.isTemporarilyDetached()) { 3037 return; 3038 } 3039 final ViewRootImpl viewRootImpl = view.getViewRootImpl(); 3040 synchronized (mH) { 3041 if (mCurRootView != viewRootImpl) { 3042 return; 3043 } 3044 if (!view.hasImeFocus() || !view.hasWindowFocus()) { 3045 return; 3046 } 3047 if (DEBUG) { 3048 Log.d(TAG, "onViewFocusChangedInternal, view=" 3049 + InputMethodDebug.dumpViewInfo(view)); 3050 } 3051 3052 // We don't need to track the next served view when the view lost focus here 3053 // because: 3054 // 1) The current view focus may be cleared temporary when in touch mode, closing 3055 // input at this moment isn't the right way. 3056 // 2) We only care about the served view change when it focused, since changing 3057 // input connection when the focus target changed is reasonable. 3058 // 3) Setting the next served view as null when no more served view should be 3059 // handled in other special events (e.g. view detached from window or the window 3060 // dismissed). 3061 if (hasFocus) { 3062 mNextServedView = view; 3063 } 3064 } 3065 viewRootImpl.dispatchCheckFocus(); 3066 } 3067 3068 @UnsupportedAppUsage closeCurrentInput()3069 void closeCurrentInput() { 3070 final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide( 3071 null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, 3072 SoftInputShowHideReason.HIDE_SOFT_INPUT); 3073 ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, 3074 SoftInputShowHideReason.HIDE_SOFT_INPUT, ActivityThread::currentApplication); 3075 3076 synchronized (mH) { 3077 if (mCurRootView == null || mCurRootView.getView() == null) { 3078 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 3079 ImeTracker.forLatency().onHideFailed(statsToken, 3080 ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication); 3081 Log.w(TAG, "No current root view, ignoring closeCurrentInput()"); 3082 return; 3083 } 3084 3085 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 3086 3087 IInputMethodManagerGlobalInvoker.hideSoftInput( 3088 mClient, 3089 mCurRootView.getView().getWindowToken(), 3090 statsToken, 3091 HIDE_NOT_ALWAYS, 3092 null, 3093 SoftInputShowHideReason.HIDE_SOFT_INPUT); 3094 } 3095 } 3096 3097 /** 3098 * Register for IME state callbacks and applying visibility in 3099 * {@link android.view.ImeInsetsSourceConsumer}. 3100 * @hide 3101 */ registerImeConsumer(@onNull ImeInsetsSourceConsumer imeInsetsConsumer)3102 public void registerImeConsumer(@NonNull ImeInsetsSourceConsumer imeInsetsConsumer) { 3103 if (imeInsetsConsumer == null) { 3104 throw new IllegalStateException("ImeInsetsSourceConsumer cannot be null."); 3105 } 3106 3107 synchronized (mH) { 3108 mImeInsetsConsumer = imeInsetsConsumer; 3109 } 3110 } 3111 3112 /** 3113 * Unregister for IME state callbacks and applying visibility in 3114 * {@link android.view.ImeInsetsSourceConsumer}. 3115 * @hide 3116 */ unregisterImeConsumer(@onNull ImeInsetsSourceConsumer imeInsetsConsumer)3117 public void unregisterImeConsumer(@NonNull ImeInsetsSourceConsumer imeInsetsConsumer) { 3118 if (imeInsetsConsumer == null) { 3119 throw new IllegalStateException("ImeInsetsSourceConsumer cannot be null."); 3120 } 3121 3122 synchronized (mH) { 3123 if (mImeInsetsConsumer == imeInsetsConsumer) { 3124 mImeInsetsConsumer = null; 3125 } 3126 } 3127 } 3128 3129 /** 3130 * Call showSoftInput with currently focused view. 3131 * 3132 * @param windowToken the window from which this request originates. If this doesn't match the 3133 * currently served view, the request is ignored and returns {@code false}. 3134 * @param statsToken the token tracking the current IME show request or {@code null} otherwise. 3135 * 3136 * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise. 3137 * @hide 3138 */ requestImeShow(IBinder windowToken, @Nullable ImeTracker.Token statsToken)3139 public boolean requestImeShow(IBinder windowToken, @Nullable ImeTracker.Token statsToken) { 3140 checkFocus(); 3141 synchronized (mH) { 3142 final View servedView = getServedViewLocked(); 3143 if (servedView == null || servedView.getWindowToken() != windowToken) { 3144 ImeTracker.forLogging().onFailed(statsToken, 3145 ImeTracker.PHASE_CLIENT_REQUEST_IME_SHOW); 3146 return false; 3147 } 3148 3149 ImeTracker.forLogging().onProgress(statsToken, 3150 ImeTracker.PHASE_CLIENT_REQUEST_IME_SHOW); 3151 3152 showSoftInput(servedView, statsToken, 0 /* flags */, null /* resultReceiver */, 3153 SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API); 3154 return true; 3155 } 3156 } 3157 3158 /** 3159 * Notify IMMS that IME insets are no longer visible. 3160 * 3161 * @param windowToken the window from which this request originates. If this doesn't match the 3162 * currently served view, the request is ignored. 3163 * @param statsToken the token tracking the current IME show request or {@code null} otherwise. 3164 * @hide 3165 */ notifyImeHidden(IBinder windowToken, @Nullable ImeTracker.Token statsToken)3166 public void notifyImeHidden(IBinder windowToken, @Nullable ImeTracker.Token statsToken) { 3167 if (statsToken == null) { 3168 statsToken = ImeTracker.forLogging().onRequestHide(null /* component */, 3169 Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, 3170 SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API); 3171 } 3172 ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, 3173 SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, 3174 ActivityThread::currentApplication); 3175 ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this, 3176 null /* icProto */); 3177 synchronized (mH) { 3178 if (!isImeSessionAvailableLocked() || mCurRootView == null 3179 || mCurRootView.getWindowToken() != windowToken) { 3180 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 3181 ImeTracker.forLatency().onHideFailed(statsToken, 3182 ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication); 3183 return; 3184 } 3185 3186 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); 3187 3188 IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken, 3189 0 /* flags */, null /* resultReceiver */, 3190 SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API); 3191 } 3192 } 3193 3194 /** 3195 * Notify IME directly to remove surface as it is no longer visible. 3196 * @param windowToken The client window token that requests the IME to remove its surface. 3197 * @hide 3198 */ removeImeSurface(@onNull IBinder windowToken)3199 public void removeImeSurface(@NonNull IBinder windowToken) { 3200 synchronized (mH) { 3201 IInputMethodManagerGlobalInvoker.removeImeSurfaceFromWindowAsync(windowToken); 3202 } 3203 } 3204 3205 /** 3206 * Report the current selection range. 3207 * 3208 * <p><strong>Editor authors</strong>, you need to call this method whenever 3209 * the cursor moves in your editor. Remember that in addition to doing this, your 3210 * editor needs to always supply current cursor values in 3211 * {@link EditorInfo#initialSelStart} and {@link EditorInfo#initialSelEnd} every 3212 * time {@link android.view.View#onCreateInputConnection(EditorInfo)} is 3213 * called, which happens whenever the keyboard shows up or the focus changes 3214 * to a text field, among other cases.</p> 3215 */ updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd)3216 public void updateSelection(View view, int selStart, int selEnd, 3217 int candidatesStart, int candidatesEnd) { 3218 // Re-dispatch if there is a context mismatch. 3219 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 3220 if (fallbackImm != null) { 3221 fallbackImm.updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd); 3222 return; 3223 } 3224 3225 checkFocus(); 3226 synchronized (mH) { 3227 if (!hasServedByInputMethodLocked(view) || mCurrentEditorInfo == null 3228 || !isImeSessionAvailableLocked()) { 3229 return; 3230 } 3231 3232 if (mServedInputConnection != null && mServedInputConnection.hasPendingInvalidation()) { 3233 return; 3234 } 3235 3236 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd 3237 || mCursorCandStart != candidatesStart 3238 || mCursorCandEnd != candidatesEnd) { 3239 if (DEBUG) Log.d(TAG, "updateSelection"); 3240 3241 if (DEBUG) { 3242 Log.v(TAG, "SELECTION CHANGE: " + mCurBindState.mImeSession); 3243 } 3244 mCurBindState.mImeSession.updateSelection(mCursorSelStart, mCursorSelEnd, selStart, 3245 selEnd, candidatesStart, candidatesEnd); 3246 forAccessibilitySessionsLocked(wrapper -> wrapper.updateSelection(mCursorSelStart, 3247 mCursorSelEnd, selStart, selEnd, candidatesStart, candidatesEnd)); 3248 mCursorSelStart = selStart; 3249 mCursorSelEnd = selEnd; 3250 mCursorCandStart = candidatesStart; 3251 mCursorCandEnd = candidatesEnd; 3252 } 3253 } 3254 } 3255 3256 /** 3257 * Notify the event when the user tapped or clicked the text view. 3258 * 3259 * @param view {@link View} which is being clicked. 3260 * @see InputMethodService#onViewClicked(boolean) 3261 * @deprecated The semantics of this method can never be defined well for composite {@link View} 3262 * that works as a giant "Canvas", which can host its own UI hierarchy and sub focus 3263 * state. {@link android.webkit.WebView} is a good example. Application / IME 3264 * developers should not rely on this method. 3265 */ 3266 @Deprecated viewClicked(View view)3267 public void viewClicked(View view) { 3268 // Re-dispatch if there is a context mismatch. 3269 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 3270 if (fallbackImm != null) { 3271 fallbackImm.viewClicked(view); 3272 return; 3273 } 3274 3275 final View servedView; 3276 final View nextServedView; 3277 synchronized (mH) { 3278 servedView = getServedViewLocked(); 3279 nextServedView = getNextServedViewLocked(); 3280 } 3281 final boolean focusChanged = servedView != nextServedView; 3282 checkFocus(); 3283 synchronized (mH) { 3284 if (!hasServedByInputMethodLocked(view) || mCurrentEditorInfo == null 3285 || !isImeSessionAvailableLocked()) { 3286 return; 3287 } 3288 if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged); 3289 mCurBindState.mImeSession.viewClicked(focusChanged); 3290 } 3291 } 3292 3293 /** 3294 * Return true if the current input method wants to watch the location 3295 * of the input editor's cursor in its window. 3296 * 3297 * @deprecated Use {@link InputConnection#requestCursorUpdates(int)} instead. 3298 */ 3299 @Deprecated isWatchingCursor(View view)3300 public boolean isWatchingCursor(View view) { 3301 return false; 3302 } 3303 3304 /** 3305 * Return true if the current input method wants to be notified when cursor/anchor location 3306 * is changed. 3307 * 3308 * @deprecated This method is kept for {@link UnsupportedAppUsage}. Must not be used. 3309 * @hide 3310 */ 3311 @Deprecated 3312 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isCursorAnchorInfoEnabled()3313 public boolean isCursorAnchorInfoEnabled() { 3314 synchronized (mH) { 3315 final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode & 3316 CURSOR_UPDATE_IMMEDIATE) != 0; 3317 final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode & 3318 CURSOR_UPDATE_MONITOR) != 0; 3319 return isImmediate || isMonitoring; 3320 } 3321 } 3322 3323 /** 3324 * Set the requested mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}. 3325 * 3326 * @deprecated This method is kept for {@link UnsupportedAppUsage}. Must not be used. 3327 * @hide 3328 */ 3329 @Deprecated 3330 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) setUpdateCursorAnchorInfoMode(int flags)3331 public void setUpdateCursorAnchorInfoMode(int flags) { 3332 synchronized (mH) { 3333 mRequestUpdateCursorAnchorInfoMonitorMode = flags; 3334 } 3335 } 3336 3337 /** 3338 * Report the current cursor location in its window. 3339 * 3340 * @deprecated Use {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} instead. 3341 */ 3342 @Deprecated updateCursor(View view, int left, int top, int right, int bottom)3343 public void updateCursor(View view, int left, int top, int right, int bottom) { 3344 // Re-dispatch if there is a context mismatch. 3345 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 3346 if (fallbackImm != null) { 3347 fallbackImm.updateCursor(view, left, top, right, bottom); 3348 return; 3349 } 3350 3351 checkFocus(); 3352 synchronized (mH) { 3353 if (!hasServedByInputMethodLocked(view) || mCurrentEditorInfo == null 3354 || !isImeSessionAvailableLocked()) { 3355 return; 3356 } 3357 3358 mTmpCursorRect.set(left, top, right, bottom); 3359 if (!mCursorRect.equals(mTmpCursorRect)) { 3360 if (DEBUG) Log.d(TAG, "updateCursor: " + mCurBindState.mImeSession); 3361 3362 mCurBindState.mImeSession.updateCursor(mTmpCursorRect); 3363 mCursorRect.set(mTmpCursorRect); 3364 } 3365 } 3366 } 3367 3368 /** 3369 * Report positional change of the text insertion point and/or characters in the composition 3370 * string. 3371 */ updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo)3372 public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) { 3373 if (view == null || cursorAnchorInfo == null) { 3374 return; 3375 } 3376 // Re-dispatch if there is a context mismatch. 3377 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 3378 if (fallbackImm != null) { 3379 fallbackImm.updateCursorAnchorInfo(view, cursorAnchorInfo); 3380 return; 3381 } 3382 3383 checkFocus(); 3384 synchronized (mH) { 3385 if (!hasServedByInputMethodLocked(view) || mCurrentEditorInfo == null 3386 || !isImeSessionAvailableLocked()) { 3387 return; 3388 } 3389 // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has 3390 // not been changed from the previous call. 3391 final boolean isImmediate = mServedInputConnection != null 3392 && mServedInputConnection.resetHasPendingImmediateCursorAnchorInfoUpdate(); 3393 if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) { 3394 // TODO: Consider always emitting this message once we have addressed redundant 3395 // calls of this method from android.widget.Editor. 3396 if (DEBUG) { 3397 Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" 3398 + cursorAnchorInfo); 3399 } 3400 return; 3401 } 3402 if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo); 3403 if (mVirtualDisplayToScreenMatrix != null) { 3404 mCurBindState.mImeSession.updateCursorAnchorInfo( 3405 CursorAnchorInfo.createForAdditionalParentMatrix( 3406 cursorAnchorInfo, mVirtualDisplayToScreenMatrix)); 3407 } else { 3408 mCurBindState.mImeSession.updateCursorAnchorInfo(cursorAnchorInfo); 3409 } 3410 mCursorAnchorInfo = cursorAnchorInfo; 3411 } 3412 } 3413 3414 /** 3415 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) 3416 * InputMethodSession.appPrivateCommand()} on the current Input Method. 3417 * @param view Optional View that is sending the command, or null if 3418 * you want to send the command regardless of the view that is attached 3419 * to the input method. 3420 * @param action Name of the command to be performed. This <em>must</em> 3421 * be a scoped name, i.e. prefixed with a package name you own, so that 3422 * different developers will not create conflicting commands. 3423 * @param data Any data to include with the command. 3424 */ sendAppPrivateCommand(View view, String action, Bundle data)3425 public void sendAppPrivateCommand(View view, String action, Bundle data) { 3426 // Re-dispatch if there is a context mismatch. 3427 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); 3428 if (fallbackImm != null) { 3429 fallbackImm.sendAppPrivateCommand(view, action, data); 3430 return; 3431 } 3432 3433 checkFocus(); 3434 synchronized (mH) { 3435 if (!hasServedByInputMethodLocked(view) || mCurrentEditorInfo == null 3436 || !isImeSessionAvailableLocked()) { 3437 return; 3438 } 3439 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); 3440 mCurBindState.mImeSession.appPrivateCommand(action, data); 3441 } 3442 } 3443 3444 /** 3445 * Force switch to a new input method component. This can only be called 3446 * from an application or a service which has a token of the currently active input method. 3447 * 3448 * <p>On Android {@link Build.VERSION_CODES#Q} and later devices, the undocumented behavior that 3449 * token can be {@code null} when the caller has 3450 * {@link Manifest.permission#WRITE_SECURE_SETTINGS} is deprecated. Instead, update 3451 * {@link android.provider.Settings.Secure#DEFAULT_INPUT_METHOD} and 3452 * {@link android.provider.Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE} directly.</p> 3453 * 3454 * @param token Supplies the identifying token given to an input method 3455 * when it was started, which allows it to perform this operation on 3456 * itself. 3457 * @param id The unique identifier for the new input method to be switched to. 3458 * @throws IllegalArgumentException if the input method is unknown or filtered by the rules of 3459 * <a href="/training/basics/intents/package-visibility">package visibility</a>. 3460 * @deprecated Use {@link InputMethodService#switchInputMethod(String)} 3461 * instead. This method was intended for IME developers who should be accessing APIs through 3462 * the service. APIs in this class are intended for app developers interacting with the IME. 3463 */ 3464 @Deprecated setInputMethod(IBinder token, String id)3465 public void setInputMethod(IBinder token, String id) { 3466 if (token == null) { 3467 // There are still some system components that rely on this undocumented behavior 3468 // regarding null IME token with WRITE_SECURE_SETTINGS. Provide a fallback logic as a 3469 // temporary remedy. 3470 if (id == null) { 3471 return; 3472 } 3473 if (Process.myUid() == Process.SYSTEM_UID) { 3474 Log.w(TAG, "System process should not be calling setInputMethod() because almost " 3475 + "always it is a bug under multi-user / multi-profile environment. " 3476 + "Consider interacting with InputMethodManagerService directly via " 3477 + "LocalServices."); 3478 return; 3479 } 3480 final Context fallbackContext = ActivityThread.currentApplication(); 3481 if (fallbackContext == null) { 3482 return; 3483 } 3484 if (fallbackContext.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) 3485 != PackageManager.PERMISSION_GRANTED) { 3486 return; 3487 } 3488 final List<InputMethodInfo> imis = getEnabledInputMethodList(); 3489 final int numImis = imis.size(); 3490 boolean found = false; 3491 for (int i = 0; i < numImis; ++i) { 3492 final InputMethodInfo imi = imis.get(i); 3493 if (id.equals(imi.getId())) { 3494 found = true; 3495 break; 3496 } 3497 } 3498 if (!found) { 3499 Log.e(TAG, "Ignoring setInputMethod(null, " + id + ") because the specified " 3500 + "id not found in enabled IMEs."); 3501 return; 3502 } 3503 Log.w(TAG, "The undocumented behavior that setInputMethod() accepts null token " 3504 + "when the caller has WRITE_SECURE_SETTINGS is deprecated. This behavior may " 3505 + "be completely removed in a future version. Update secure settings directly " 3506 + "instead."); 3507 final ContentResolver resolver = fallbackContext.getContentResolver(); 3508 Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, 3509 NOT_A_SUBTYPE_ID); 3510 Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD, id); 3511 return; 3512 } 3513 InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id); 3514 } 3515 3516 /** 3517 * Force switch to a new input method and subtype. This can only be called 3518 * from an application or a service which has a token of the currently active input method. 3519 * 3520 * <p>On Android {@link Build.VERSION_CODES#Q} and later devices, {@code token} cannot be 3521 * {@code null} even with {@link Manifest.permission#WRITE_SECURE_SETTINGS}. Instead, 3522 * update {@link android.provider.Settings.Secure#DEFAULT_INPUT_METHOD} and 3523 * {@link android.provider.Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE} directly.</p> 3524 * 3525 * @param token Supplies the identifying token given to an input method 3526 * when it was started, which allows it to perform this operation on 3527 * itself. 3528 * @param id The unique identifier for the new input method to be switched to. 3529 * @param subtype The new subtype of the new input method to be switched to. 3530 * @throws IllegalArgumentException if the input method is unknown or filtered by the rules of 3531 * <a href="/training/basics/intents/package-visibility">package visibility</a>. 3532 * @deprecated Use 3533 * {@link InputMethodService#switchInputMethod(String, InputMethodSubtype)} 3534 * instead. This method was intended for IME developers who should be accessing APIs through 3535 * the service. APIs in this class are intended for app developers interacting with the IME. 3536 */ 3537 @Deprecated setInputMethodAndSubtype(@onNull IBinder token, String id, InputMethodSubtype subtype)3538 public void setInputMethodAndSubtype(@NonNull IBinder token, String id, 3539 InputMethodSubtype subtype) { 3540 if (token == null) { 3541 Log.e(TAG, "setInputMethodAndSubtype() does not accept null token on Android Q " 3542 + "and later."); 3543 return; 3544 } 3545 InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype); 3546 } 3547 3548 /** 3549 * Close/hide the input method's soft input area, so the user no longer 3550 * sees it or can interact with it. This can only be called 3551 * from the currently active input method, as validated by the given token. 3552 * 3553 * @param token Supplies the identifying token given to an input method 3554 * when it was started, which allows it to perform this operation on 3555 * itself. 3556 * @deprecated Use {@link InputMethodService#requestHideSelf(int)} instead. This method was 3557 * intended for IME developers who should be accessing APIs through the service. APIs in this 3558 * class are intended for app developers interacting with the IME. 3559 */ 3560 @Deprecated hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags)3561 public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) { 3562 InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput( 3563 flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION); 3564 } 3565 3566 /** 3567 * Show the input method's soft input area, so the user 3568 * sees the input method window and can interact with it. 3569 * This can only be called from the currently active input method, 3570 * as validated by the given token. 3571 * 3572 * @param token Supplies the identifying token given to an input method 3573 * when it was started, which allows it to perform this operation on 3574 * itself. 3575 * @deprecated Use {@link InputMethodService#requestShowSelf(int)} instead. This method was 3576 * intended for IME developers who should be accessing APIs through the service. APIs in this 3577 * class are intended for app developers interacting with the IME. 3578 */ 3579 @Deprecated showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags)3580 public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) { 3581 InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags); 3582 } 3583 3584 /** 3585 * Dispatches an input event to the IME. 3586 * 3587 * Returns {@link #DISPATCH_HANDLED} if the event was handled. 3588 * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled. 3589 * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the 3590 * callback will be invoked later. 3591 * 3592 * @hide 3593 */ dispatchInputEvent(InputEvent event, Object token, FinishedInputEventCallback callback, Handler handler)3594 public int dispatchInputEvent(InputEvent event, Object token, 3595 FinishedInputEventCallback callback, Handler handler) { 3596 synchronized (mH) { 3597 if (isImeSessionAvailableLocked()) { 3598 if (event instanceof KeyEvent) { 3599 KeyEvent keyEvent = (KeyEvent)event; 3600 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN 3601 && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM 3602 && keyEvent.getRepeatCount() == 0) { 3603 showInputMethodPickerLocked(); 3604 return DISPATCH_HANDLED; 3605 } 3606 } 3607 3608 if (DEBUG) { 3609 Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurBindState.mImeSession); 3610 } 3611 3612 PendingEvent p = obtainPendingEventLocked( 3613 event, token, mCurBindState.mImeId, callback, handler); 3614 if (mMainLooper.isCurrentThread()) { 3615 // Already running on the IMM thread so we can send the event immediately. 3616 return sendInputEventOnMainLooperLocked(p); 3617 } 3618 3619 // Post the event to the IMM thread. 3620 Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p); 3621 msg.setAsynchronous(true); 3622 mH.sendMessage(msg); 3623 return DISPATCH_IN_PROGRESS; 3624 } 3625 } 3626 return DISPATCH_NOT_HANDLED; 3627 } 3628 3629 /** 3630 * Provides the default implementation of {@link InputConnection#sendKeyEvent(KeyEvent)}, which 3631 * is expected to dispatch an keyboard event sent from the IME to an appropriate event target 3632 * depending on the given {@link View} and the current focus state. 3633 * 3634 * <p>CAUTION: This method is provided only for the situation where 3635 * {@link InputConnection#sendKeyEvent(KeyEvent)} needs to be implemented without relying on 3636 * {@link BaseInputConnection}. Do not use this API for anything else.</p> 3637 * 3638 * @param targetView the default target view. If {@code null} is specified, then this method 3639 * tries to find a good event target based on the current focus state. 3640 * @param event the key event to be dispatched. 3641 */ dispatchKeyEventFromInputMethod(@ullable View targetView, @NonNull KeyEvent event)3642 public void dispatchKeyEventFromInputMethod(@Nullable View targetView, 3643 @NonNull KeyEvent event) { 3644 // Re-dispatch if there is a context mismatch. 3645 final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(targetView); 3646 if (fallbackImm != null) { 3647 fallbackImm.dispatchKeyEventFromInputMethod(targetView, event); 3648 return; 3649 } 3650 3651 synchronized (mH) { 3652 ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; 3653 if (viewRootImpl == null) { 3654 final View servedView = getServedViewLocked(); 3655 if (servedView != null) { 3656 viewRootImpl = servedView.getViewRootImpl(); 3657 } 3658 } 3659 if (viewRootImpl != null) { 3660 viewRootImpl.dispatchKeyFromIme(event); 3661 } 3662 } 3663 } 3664 3665 // Must be called on the main looper sendInputEventAndReportResultOnMainLooper(PendingEvent p)3666 private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) { 3667 final boolean handled; 3668 synchronized (mH) { 3669 int result = sendInputEventOnMainLooperLocked(p); 3670 if (result == DISPATCH_IN_PROGRESS) { 3671 return; 3672 } 3673 3674 handled = (result == DISPATCH_HANDLED); 3675 } 3676 3677 invokeFinishedInputEventCallback(p, handled); 3678 } 3679 3680 // Must be called on the main looper 3681 @GuardedBy("mH") sendInputEventOnMainLooperLocked(PendingEvent p)3682 private int sendInputEventOnMainLooperLocked(PendingEvent p) { 3683 if (mCurChannel != null) { 3684 if (mCurSender == null) { 3685 mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper()); 3686 } 3687 3688 final InputEvent event = p.mEvent; 3689 final int seq = event.getSequenceNumber(); 3690 if (mCurSender.sendInputEvent(seq, event)) { 3691 mPendingEvents.put(seq, p); 3692 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, 3693 mPendingEvents.size()); 3694 3695 Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, seq, 0, p); 3696 msg.setAsynchronous(true); 3697 mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT); 3698 return DISPATCH_IN_PROGRESS; 3699 } 3700 3701 if (sPreventImeStartupUnlessTextEditor) { 3702 Log.d(TAG, "Dropping event because IME is evicted: " + event); 3703 } else { 3704 Log.w(TAG, "Unable to send input event to IME: " + getImeIdLocked() 3705 + " dropping: " + event); 3706 } 3707 } 3708 return DISPATCH_NOT_HANDLED; 3709 } 3710 finishedInputEvent(int seq, boolean handled, boolean timeout)3711 private void finishedInputEvent(int seq, boolean handled, boolean timeout) { 3712 final PendingEvent p; 3713 synchronized (mH) { 3714 int index = mPendingEvents.indexOfKey(seq); 3715 if (index < 0) { 3716 return; // spurious, event already finished or timed out 3717 } 3718 3719 p = mPendingEvents.valueAt(index); 3720 mPendingEvents.removeAt(index); 3721 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size()); 3722 3723 if (timeout) { 3724 Log.w(TAG, "Timeout waiting for IME to handle input event after " 3725 + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId); 3726 } else { 3727 mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p); 3728 } 3729 } 3730 3731 invokeFinishedInputEventCallback(p, handled); 3732 } 3733 3734 // Assumes the event has already been removed from the queue. invokeFinishedInputEventCallback(PendingEvent p, boolean handled)3735 private void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) { 3736 p.mHandled = handled; 3737 if (p.mHandler.getLooper().isCurrentThread()) { 3738 // Already running on the callback handler thread so we can send the 3739 // callback immediately. 3740 p.run(); 3741 } else { 3742 // Post the event to the callback handler thread. 3743 // In this case, the callback will be responsible for recycling the event. 3744 Message msg = Message.obtain(p.mHandler, p); 3745 msg.setAsynchronous(true); 3746 msg.sendToTarget(); 3747 } 3748 } 3749 3750 @GuardedBy("mH") flushPendingEventsLocked()3751 private void flushPendingEventsLocked() { 3752 mH.removeMessages(MSG_FLUSH_INPUT_EVENT); 3753 3754 final int count = mPendingEvents.size(); 3755 for (int i = 0; i < count; i++) { 3756 int seq = mPendingEvents.keyAt(i); 3757 Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0); 3758 msg.setAsynchronous(true); 3759 msg.sendToTarget(); 3760 } 3761 } 3762 3763 @GuardedBy("mH") obtainPendingEventLocked(InputEvent event, Object token, String inputMethodId, FinishedInputEventCallback callback, Handler handler)3764 private PendingEvent obtainPendingEventLocked(InputEvent event, Object token, 3765 String inputMethodId, FinishedInputEventCallback callback, Handler handler) { 3766 PendingEvent p = mPendingEventPool.acquire(); 3767 if (p == null) { 3768 p = new PendingEvent(); 3769 } 3770 p.mEvent = event; 3771 p.mToken = token; 3772 p.mInputMethodId = inputMethodId; 3773 p.mCallback = callback; 3774 p.mHandler = handler; 3775 return p; 3776 } 3777 3778 @GuardedBy("mH") recyclePendingEventLocked(PendingEvent p)3779 private void recyclePendingEventLocked(PendingEvent p) { 3780 p.recycle(); 3781 mPendingEventPool.release(p); 3782 } 3783 3784 /** 3785 * Show IME picker popup window. 3786 * 3787 * <p>Requires the {@link PackageManager#FEATURE_INPUT_METHODS} feature which can be detected 3788 * using {@link PackageManager#hasSystemFeature(String)}. 3789 */ showInputMethodPicker()3790 public void showInputMethodPicker() { 3791 synchronized (mH) { 3792 showInputMethodPickerLocked(); 3793 } 3794 } 3795 3796 /** 3797 * Shows the input method chooser dialog from system. 3798 * 3799 * @param showAuxiliarySubtypes Set true to show auxiliary input methods. 3800 * @param displayId The ID of the display where the chooser dialog should be shown. 3801 * @hide 3802 */ 3803 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) showInputMethodPickerFromSystem(boolean showAuxiliarySubtypes, int displayId)3804 public void showInputMethodPickerFromSystem(boolean showAuxiliarySubtypes, int displayId) { 3805 final int mode = showAuxiliarySubtypes 3806 ? SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES 3807 : SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES; 3808 IInputMethodManagerGlobalInvoker.showInputMethodPickerFromSystem(mode, displayId); 3809 } 3810 3811 @GuardedBy("mH") showInputMethodPickerLocked()3812 private void showInputMethodPickerLocked() { 3813 IInputMethodManagerGlobalInvoker.showInputMethodPickerFromClient(mClient, 3814 SHOW_IM_PICKER_MODE_AUTO); 3815 } 3816 3817 /** 3818 * A test API for CTS to make sure that {@link #showInputMethodPicker()} works as expected. 3819 * 3820 * <p>When customizing the implementation of {@link #showInputMethodPicker()} API, make sure 3821 * that this test API returns when and only while and only while 3822 * {@link #showInputMethodPicker()} is showing UI. Otherwise your OS implementation may not 3823 * pass CTS.</p> 3824 * 3825 * @return {@code true} while and only while {@link #showInputMethodPicker()} is showing UI. 3826 * @hide 3827 */ 3828 @TestApi 3829 @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) isInputMethodPickerShown()3830 public boolean isInputMethodPickerShown() { 3831 return IInputMethodManagerGlobalInvoker.isInputMethodPickerShownForTest(); 3832 } 3833 3834 /** 3835 * A test API for CTS to check whether there are any pending IME visibility requests. 3836 * 3837 * @return {@code true} iff there are pending IME visibility requests. 3838 * @hide 3839 */ 3840 @TestApi 3841 @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) hasPendingImeVisibilityRequests()3842 public boolean hasPendingImeVisibilityRequests() { 3843 return IInputMethodManagerGlobalInvoker.hasPendingImeVisibilityRequests(); 3844 } 3845 3846 /** 3847 * Show the settings for enabling subtypes of the specified input method. 3848 * 3849 * @param imiId An input method, whose subtypes settings will be shown. If imiId is null, 3850 * subtypes of all input methods will be shown. 3851 */ showInputMethodAndSubtypeEnabler(@ullable String imiId)3852 public void showInputMethodAndSubtypeEnabler(@Nullable String imiId) { 3853 Context context = null; 3854 synchronized (mH) { 3855 if (mCurRootView != null) { 3856 context = mCurRootView.mContext; 3857 } 3858 } 3859 if (context == null) { 3860 final Context appContext = ActivityThread.currentApplication(); 3861 final DisplayManager displayManager = appContext.getSystemService(DisplayManager.class); 3862 context = appContext.createDisplayContext(displayManager.getDisplay(mDisplayId)); 3863 } 3864 3865 final Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); 3866 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 3867 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 3868 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 3869 if (!TextUtils.isEmpty(imiId)) { 3870 intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imiId); 3871 } 3872 context.startActivity(intent); 3873 } 3874 3875 /** 3876 * Returns the current input method subtype. This subtype is one of the subtypes in 3877 * the current input method. This method returns null when the current input method doesn't 3878 * have any input method subtype. 3879 */ 3880 @Nullable getCurrentInputMethodSubtype()3881 public InputMethodSubtype getCurrentInputMethodSubtype() { 3882 return IInputMethodManagerGlobalInvoker.getCurrentInputMethodSubtype(UserHandle.myUserId()); 3883 } 3884 3885 /** 3886 * Switch to a new input method subtype of the current input method. 3887 * @param subtype A new input method subtype to switch. 3888 * @return true if the current subtype was successfully switched. When the specified subtype is 3889 * null, this method returns false. 3890 * @deprecated If the calling process is an IME, use 3891 * {@link InputMethodService#switchInputMethod(String, InputMethodSubtype)}, which 3892 * does not require any permission as long as the caller is the current IME. 3893 * If the calling process is some privileged app that already has 3894 * {@link Manifest.permission#WRITE_SECURE_SETTINGS} permission, just 3895 * directly update {@link Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE}. 3896 */ 3897 @Deprecated 3898 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) setCurrentInputMethodSubtype(InputMethodSubtype subtype)3899 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { 3900 if (Process.myUid() == Process.SYSTEM_UID) { 3901 Log.w(TAG, "System process should not call setCurrentInputMethodSubtype() because " 3902 + "almost always it is a bug under multi-user / multi-profile environment. " 3903 + "Consider directly interacting with InputMethodManagerService " 3904 + "via LocalServices."); 3905 return false; 3906 } 3907 if (subtype == null) { 3908 // See the JavaDoc. This is how this method has worked. 3909 return false; 3910 } 3911 final Context fallbackContext = ActivityThread.currentApplication(); 3912 if (fallbackContext == null) { 3913 return false; 3914 } 3915 if (fallbackContext.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) 3916 != PackageManager.PERMISSION_GRANTED) { 3917 return false; 3918 } 3919 final ContentResolver contentResolver = fallbackContext.getContentResolver(); 3920 final String imeId = Settings.Secure.getString(contentResolver, 3921 Settings.Secure.DEFAULT_INPUT_METHOD); 3922 if (ComponentName.unflattenFromString(imeId) == null) { 3923 // Null or invalid IME ID format. 3924 return false; 3925 } 3926 final List<InputMethodSubtype> enabledSubtypes = 3927 IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList(imeId, true, 3928 UserHandle.myUserId()); 3929 final int numSubtypes = enabledSubtypes.size(); 3930 for (int i = 0; i < numSubtypes; ++i) { 3931 final InputMethodSubtype enabledSubtype = enabledSubtypes.get(i); 3932 if (enabledSubtype.equals(subtype)) { 3933 Settings.Secure.putInt(contentResolver, 3934 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, enabledSubtype.hashCode()); 3935 return true; 3936 } 3937 } 3938 return false; 3939 } 3940 3941 /** 3942 * Notify that a user took some action with this input method. 3943 * 3944 * @deprecated Just kept to avoid possible app compat issue. 3945 * @hide 3946 */ 3947 @Deprecated 3948 @UnsupportedAppUsage(trackingBug = 114740982, maxTargetSdk = Build.VERSION_CODES.P) notifyUserAction()3949 public void notifyUserAction() { 3950 Log.w(TAG, "notifyUserAction() is a hidden method, which is now just a stub method" 3951 + " that does nothing. Leave comments in b.android.com/114740982 if your " 3952 + " application still depends on the previous behavior of this method."); 3953 } 3954 3955 /** 3956 * Returns a map of all shortcut input method info and their subtypes. 3957 */ getShortcutInputMethodsAndSubtypes()3958 public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() { 3959 final List<InputMethodInfo> enabledImes = getEnabledInputMethodList(); 3960 3961 // Ensure we check system IMEs first. 3962 enabledImes.sort(Comparator.comparingInt(imi -> imi.isSystem() ? 0 : 1)); 3963 3964 final int numEnabledImes = enabledImes.size(); 3965 for (int imiIndex = 0; imiIndex < numEnabledImes; ++imiIndex) { 3966 final InputMethodInfo imi = enabledImes.get(imiIndex); 3967 final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeList( 3968 imi, true); 3969 final int subtypeCount = subtypes.size(); 3970 for (int subtypeIndex = 0; subtypeIndex < subtypeCount; ++subtypeIndex) { 3971 final InputMethodSubtype subtype = imi.getSubtypeAt(subtypeIndex); 3972 if (SUBTYPE_MODE_VOICE.equals(subtype.getMode())) { 3973 return Collections.singletonMap(imi, Collections.singletonList(subtype)); 3974 } 3975 } 3976 } 3977 return Collections.emptyMap(); 3978 } 3979 3980 /** 3981 * This is kept due to {@link android.compat.annotation.UnsupportedAppUsage}. 3982 * 3983 * <p>TODO(Bug 113914148): Check if we can remove this. We have accidentally exposed 3984 * WindowManagerInternal#getInputMethodWindowVisibleHeight to app developers and some of them 3985 * started relying on it.</p> 3986 * 3987 * @return Something that is not well-defined. 3988 * @hide 3989 */ 3990 @UnsupportedAppUsage(trackingBug = 204906124, maxTargetSdk = Build.VERSION_CODES.TIRAMISU, 3991 publicAlternatives = "Use {@link android.view.WindowInsets} instead") getInputMethodWindowVisibleHeight()3992 public int getInputMethodWindowVisibleHeight() { 3993 return IInputMethodManagerGlobalInvoker.getInputMethodWindowVisibleHeight(mClient); 3994 } 3995 3996 /** 3997 * {@code true} means that 3998 * {@link RemoteInputConnectionImpl#requestCursorUpdatesInternal(int, int, int)} returns 3999 * {@code false} when the IME client and the IME run in different displays. 4000 */ 4001 final AtomicBoolean mRequestCursorUpdateDisplayIdCheck = new AtomicBoolean(true); 4002 4003 /** 4004 * Controls the display ID mismatch validation in 4005 * {@link RemoteInputConnectionImpl#requestCursorUpdatesInternal(int, int, int)}. 4006 * 4007 * <p>{@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} is not guaranteed to work 4008 * correctly when the IME client and the IME run in different displays. This is why 4009 * {@link RemoteInputConnectionImpl#requestCursorUpdatesInternal(int, int, int)} returns 4010 * {@code false} by default when the display ID does not match. This method allows special apps 4011 * to override this behavior when they are sure that it should work.</p> 4012 * 4013 * <p>By default the validation is enabled.</p> 4014 * 4015 * @param enabled {@code false} to disable the display ID validation. 4016 * @hide 4017 */ setRequestCursorUpdateDisplayIdCheck(boolean enabled)4018 public void setRequestCursorUpdateDisplayIdCheck(boolean enabled) { 4019 mRequestCursorUpdateDisplayIdCheck.set(enabled); 4020 } 4021 4022 /** 4023 * An internal API for {@link android.hardware.display.VirtualDisplay} to report where its 4024 * embedded virtual display is placed. 4025 * 4026 * @param childDisplayId Display ID of the embedded virtual display. 4027 * @param matrix {@link Matrix} to convert virtual display screen coordinates to 4028 * the host screen coordinates. {@code null} to clear the relationship. 4029 * @hide 4030 */ reportVirtualDisplayGeometry(int childDisplayId, @Nullable Matrix matrix)4031 public void reportVirtualDisplayGeometry(int childDisplayId, @Nullable Matrix matrix) { 4032 final float[] matrixValues; 4033 if (matrix == null) { 4034 matrixValues = null; 4035 } else { 4036 matrixValues = new float[9]; 4037 matrix.getValues(matrixValues); 4038 } 4039 IInputMethodManagerGlobalInvoker.reportVirtualDisplayGeometryAsync(mClient, childDisplayId, 4040 matrixValues); 4041 } 4042 4043 /** 4044 * An internal API that returns if the current display has a transformation matrix to apply. 4045 * 4046 * @return {@code true} if {@link Matrix} to convert virtual display screen coordinates to 4047 * the host screen coordinates is set. 4048 * @hide 4049 */ hasVirtualDisplayToScreenMatrix()4050 public boolean hasVirtualDisplayToScreenMatrix() { 4051 synchronized (mH) { 4052 return mVirtualDisplayToScreenMatrix != null; 4053 } 4054 } 4055 4056 /** 4057 * Force switch to the last used input method and subtype. If the last input method didn't have 4058 * any subtypes, the framework will simply switch to the last input method with no subtype 4059 * specified. 4060 * @param imeToken Supplies the identifying token given to an input method when it was started, 4061 * which allows it to perform this operation on itself. 4062 * @return true if the current input method and subtype was successfully switched to the last 4063 * used input method and subtype. 4064 * @deprecated Use {@link InputMethodService#switchToPreviousInputMethod()} instead. This method 4065 * was intended for IME developers who should be accessing APIs through the service. APIs in 4066 * this class are intended for app developers interacting with the IME. 4067 */ 4068 @Deprecated switchToLastInputMethod(IBinder imeToken)4069 public boolean switchToLastInputMethod(IBinder imeToken) { 4070 return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod(); 4071 } 4072 4073 /** 4074 * Force switch to the next input method and subtype. If there is no IME enabled except 4075 * current IME and subtype, do nothing. 4076 * @param imeToken Supplies the identifying token given to an input method when it was started, 4077 * which allows it to perform this operation on itself. 4078 * @param onlyCurrentIme if true, the framework will find the next subtype which 4079 * belongs to the current IME 4080 * @return true if the current input method and subtype was successfully switched to the next 4081 * input method and subtype. 4082 * @deprecated Use {@link InputMethodService#switchToNextInputMethod(boolean)} instead. This 4083 * method was intended for IME developers who should be accessing APIs through the service. 4084 * APIs in this class are intended for app developers interacting with the IME. 4085 */ 4086 @Deprecated switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme)4087 public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) { 4088 return InputMethodPrivilegedOperationsRegistry.get(imeToken) 4089 .switchToNextInputMethod(onlyCurrentIme); 4090 } 4091 4092 /** 4093 * Returns true if the current IME needs to offer the users ways to switch to a next input 4094 * method (e.g. a globe key.). 4095 * When an IME sets supportsSwitchingToNextInputMethod and this method returns true, 4096 * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly. 4097 * <p> Note that the system determines the most appropriate next input method 4098 * and subtype in order to provide the consistent user experience in switching 4099 * between IMEs and subtypes. 4100 * @param imeToken Supplies the identifying token given to an input method when it was started, 4101 * which allows it to perform this operation on itself. 4102 * @deprecated Use {@link InputMethodService#shouldOfferSwitchingToNextInputMethod()} 4103 * instead. This method was intended for IME developers who should be accessing APIs through 4104 * the service. APIs in this class are intended for app developers interacting with the IME. 4105 */ 4106 @Deprecated shouldOfferSwitchingToNextInputMethod(IBinder imeToken)4107 public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) { 4108 return InputMethodPrivilegedOperationsRegistry.get(imeToken) 4109 .shouldOfferSwitchingToNextInputMethod(); 4110 } 4111 4112 /** 4113 * Set additional input method subtypes. Only a process which shares the same uid with the IME 4114 * can add additional input method subtypes to the IME. 4115 * Please note that a subtype's status is stored in the system. 4116 * For example, enabled subtypes are remembered by the framework even after they are removed 4117 * by using this method. If you re-add the same subtypes again, 4118 * they will just get enabled. If you want to avoid such conflicts, for instance, you may 4119 * want to create a "different" new subtype even with the same locale and mode, 4120 * by changing its extra value. The different subtype won't get affected by the stored past 4121 * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer 4122 * to the current implementation.) 4123 * 4124 * <p>NOTE: If the same subtype exists in both the manifest XML file and additional subtypes 4125 * specified by {@code subtypes}, those multiple instances are automatically merged into one 4126 * instance.</p> 4127 * 4128 * <p>CAVEAT: In API Level 23 and prior, the system may do nothing if an empty 4129 * {@link InputMethodSubtype} is specified in {@code subtypes}, which prevents you from removing 4130 * the last one entry of additional subtypes. If your IME statically defines one or more 4131 * subtypes in the manifest XML file, you may be able to work around this limitation by 4132 * specifying one of those statically defined subtypes in {@code subtypes}.</p> 4133 * 4134 * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to. 4135 * If the imiId is {@code null}, system would do nothing for this operation. 4136 * @param subtypes subtypes will be added as additional subtypes of the current input method. 4137 * If the subtypes is {@code null}, system would do nothing for this operation. 4138 * @deprecated For IMEs that have already implemented features like customizable/downloadable 4139 * keyboard layouts/languages, please start migration to other approaches. One idea 4140 * would be exposing only one unified {@link InputMethodSubtype} then implement 4141 * IME's own language switching mechanism within that unified subtype. The support 4142 * of "Additional Subtype" may be completely dropped in a future version of Android. 4143 */ 4144 @Deprecated setAdditionalInputMethodSubtypes(@onNull String imiId, @NonNull InputMethodSubtype[] subtypes)4145 public void setAdditionalInputMethodSubtypes(@NonNull String imiId, 4146 @NonNull InputMethodSubtype[] subtypes) { 4147 IInputMethodManagerGlobalInvoker.setAdditionalInputMethodSubtypes(imiId, subtypes, 4148 UserHandle.myUserId()); 4149 } 4150 4151 /** 4152 * Updates the list of explicitly enabled {@link InputMethodSubtype} for a given IME owned by 4153 * the calling process. 4154 * 4155 * <p>By default each IME has no explicitly enabled {@link InputMethodSubtype}. In this state 4156 * the system will decide what {@link InputMethodSubtype} should be enabled by using information 4157 * available at runtime as per-user language settings. Users can, however, manually pick up one 4158 * or more {@link InputMethodSubtype} to be enabled on an Activity shown by 4159 * {@link #showInputMethodAndSubtypeEnabler(String)}. Such a manual change is stored in 4160 * {@link Settings.Secure#ENABLED_INPUT_METHODS} so that the change can persist across reboots. 4161 * {@link Settings.Secure#ENABLED_INPUT_METHODS} stores {@link InputMethodSubtype#hashCode()} as 4162 * the identifier of {@link InputMethodSubtype} for historical reasons.</p> 4163 * 4164 * <p>This API provides a safe and managed way for IME developers to modify what 4165 * {@link InputMethodSubtype} are referenced in {@link Settings.Secure#ENABLED_INPUT_METHODS} 4166 * for their own IME. One use case is when IME developers want to use their own Activity for 4167 * users to pick up {@link InputMethodSubtype}. Another use case is for IME developers to fix up 4168 * any stale and/or invalid value stored in {@link Settings.Secure#ENABLED_INPUT_METHODS} 4169 * without bothering users. Passing an empty {@code subtypeHashCodes} is guaranteed to reset 4170 * the state to default.</p> 4171 * 4172 * <h3>To control the return value of {@link InputMethodSubtype#hashCode()}</h3> 4173 * <p>{@link android.R.attr#subtypeId} and {@link 4174 * android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder#setSubtypeId(int)} are 4175 * available for IME developers to control the return value of 4176 * {@link InputMethodSubtype#hashCode()}. Beware that {@code -1} is not a valid value of 4177 * {@link InputMethodSubtype#hashCode()} for historical reasons.</p> 4178 * 4179 * <h3>Note for Direct Boot support</h3> 4180 * <p>While IME developers can call this API even before 4181 * {@link android.os.UserManager#isUserUnlocked()} becomes {@code true}, such a change is 4182 * volatile thus remains effective only until {@link android.os.UserManager#isUserUnlocked()} 4183 * becomes {@code true} or the device is rebooted. To make the change persistent IME developers 4184 * need to call this API again after receiving {@link Intent#ACTION_USER_UNLOCKED}.</p> 4185 * 4186 * @param imiId IME ID. The specified IME and the calling process need to belong to the same 4187 * package. Otherwise {@link SecurityException} will be thrown. 4188 * @param subtypeHashCodes An arrays of {@link InputMethodSubtype#hashCode()} to be explicitly 4189 * enabled. Entries that are found in the specified IME will be silently 4190 * ignored. Pass an empty array to reset the state to default. 4191 * @throws NullPointerException if {@code subtypeHashCodes} is {@code null}. 4192 * @throws SecurityException if the specified IME and the calling process do not belong to the 4193 * same package. 4194 */ setExplicitlyEnabledInputMethodSubtypes(@onNull String imiId, @NonNull int[] subtypeHashCodes)4195 public void setExplicitlyEnabledInputMethodSubtypes(@NonNull String imiId, 4196 @NonNull int[] subtypeHashCodes) { 4197 IInputMethodManagerGlobalInvoker.setExplicitlyEnabledInputMethodSubtypes(imiId, 4198 subtypeHashCodes, UserHandle.myUserId()); 4199 } 4200 4201 /** 4202 * Returns the last used {@link InputMethodSubtype} in system history. 4203 * 4204 * @return the last {@link InputMethodSubtype}, {@code null} if last IME have no subtype. 4205 */ 4206 @Nullable getLastInputMethodSubtype()4207 public InputMethodSubtype getLastInputMethodSubtype() { 4208 return IInputMethodManagerGlobalInvoker.getLastInputMethodSubtype(UserHandle.myUserId()); 4209 } 4210 4211 /** 4212 * <p>This is used for CTS test only. Do not use this method outside of CTS package.<p/> 4213 * @return the ID of this display which this {@link InputMethodManager} resides 4214 * @hide 4215 */ 4216 @TestApi getDisplayId()4217 public int getDisplayId() { 4218 return mDisplayId; 4219 } 4220 doDump(FileDescriptor fd, PrintWriter fout, String[] args)4221 private void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { 4222 if (processDump(fd, args)) { 4223 return; 4224 } 4225 4226 final Printer p = new PrintWriterPrinter(fout); 4227 p.println("Input method client state for " + this + ":"); 4228 p.println(" mFallbackInputConnection=" + mFallbackInputConnection); 4229 p.println(" mActive=" + mActive 4230 + " mRestartOnNextWindowFocus=" + mRestartOnNextWindowFocus 4231 + " mBindSequence=" + getBindSequenceLocked() 4232 + " mCurImeId=" + getImeIdLocked()); 4233 p.println(" mFullscreenMode=" + mFullscreenMode); 4234 if (isImeSessionAvailableLocked()) { 4235 p.println(" mCurMethod=" + mCurBindState.mImeSession); 4236 } else { 4237 p.println(" mCurMethod= null"); 4238 } 4239 for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) { 4240 p.println(" mAccessibilityInputMethodSession(" 4241 + mAccessibilityInputMethodSession.keyAt(i) + ")=" 4242 + mAccessibilityInputMethodSession.valueAt(i)); 4243 } 4244 p.println(" mCurRootView=" + mCurRootView); 4245 p.println(" mServedView=" + getServedViewLocked()); 4246 p.println(" mNextServedView=" + getNextServedViewLocked()); 4247 p.println(" mServedConnecting=" + mServedConnecting); 4248 if (mCurrentEditorInfo != null) { 4249 p.println(" mCurrentEditorInfo:"); 4250 mCurrentEditorInfo.dump(p, " ", false /* dumpExtras */); 4251 } else { 4252 p.println(" mCurrentEditorInfo: null"); 4253 } 4254 p.println(" mServedInputConnection=" + mServedInputConnection); 4255 p.println(" mServedInputConnectionHandler=" + mServedInputConnectionHandler); 4256 p.println(" mCompletions=" + Arrays.toString(mCompletions)); 4257 p.println(" mCursorRect=" + mCursorRect); 4258 p.println(" mCursorSelStart=" + mCursorSelStart 4259 + " mCursorSelEnd=" + mCursorSelEnd 4260 + " mCursorCandStart=" + mCursorCandStart 4261 + " mCursorCandEnd=" + mCursorCandEnd); 4262 } 4263 4264 /** 4265 * Callback that is invoked when an input event that was dispatched to 4266 * the IME has been finished. 4267 * @hide 4268 */ 4269 public interface FinishedInputEventCallback { onFinishedInputEvent(Object token, boolean handled)4270 public void onFinishedInputEvent(Object token, boolean handled); 4271 } 4272 4273 private final class ImeInputEventSender extends InputEventSender { ImeInputEventSender(InputChannel inputChannel, Looper looper)4274 public ImeInputEventSender(InputChannel inputChannel, Looper looper) { 4275 super(inputChannel, looper); 4276 } 4277 4278 @Override onInputEventFinished(int seq, boolean handled)4279 public void onInputEventFinished(int seq, boolean handled) { 4280 finishedInputEvent(seq, handled, false); 4281 } 4282 } 4283 4284 private final class PendingEvent implements Runnable { 4285 public InputEvent mEvent; 4286 public Object mToken; 4287 public String mInputMethodId; 4288 public FinishedInputEventCallback mCallback; 4289 public Handler mHandler; 4290 public boolean mHandled; 4291 recycle()4292 public void recycle() { 4293 mEvent = null; 4294 mToken = null; 4295 mInputMethodId = null; 4296 mCallback = null; 4297 mHandler = null; 4298 mHandled = false; 4299 } 4300 4301 @Override run()4302 public void run() { 4303 mCallback.onFinishedInputEvent(mToken, mHandled); 4304 4305 synchronized (mH) { 4306 recyclePendingEventLocked(this); 4307 } 4308 } 4309 } 4310 4311 private static final class BindState { 4312 /** 4313 * Encapsulates IPCs to the currently connected InputMethodService. 4314 */ 4315 @Nullable 4316 final IInputMethodSessionInvoker mImeSession; 4317 4318 /** 4319 * As reported by {@link InputBindResult}. This value is determined by 4320 * {@link com.android.internal.R.styleable#InputMethod_suppressesSpellChecker}. 4321 */ 4322 final boolean mIsInputMethodSuppressingSpellChecker; 4323 4324 /** 4325 * As reported by {@link InputBindResult}. This value indicates the bound input method ID. 4326 */ 4327 @Nullable 4328 final String mImeId; 4329 4330 /** 4331 * Sequence number of this binding, as returned by the server. 4332 */ 4333 final int mBindSequence; 4334 BindState(@onNull InputBindResult inputBindResult)4335 BindState(@NonNull InputBindResult inputBindResult) { 4336 mImeSession = IInputMethodSessionInvoker.createOrNull(inputBindResult.method); 4337 mIsInputMethodSuppressingSpellChecker = 4338 inputBindResult.isInputMethodSuppressingSpellChecker; 4339 mImeId = inputBindResult.id; 4340 mBindSequence = inputBindResult.sequence; 4341 } 4342 } 4343 4344 @GuardedBy("mH") isImeSessionAvailableLocked()4345 private boolean isImeSessionAvailableLocked() { 4346 return mCurBindState != null && mCurBindState.mImeSession != null; 4347 } 4348 4349 @GuardedBy("mH") getImeIdLocked()4350 private String getImeIdLocked() { 4351 return mCurBindState != null ? mCurBindState.mImeId : null; 4352 } 4353 4354 @GuardedBy("mH") getBindSequenceLocked()4355 private int getBindSequenceLocked() { 4356 return mCurBindState != null ? mCurBindState.mBindSequence : -1; 4357 } 4358 4359 /** 4360 * Checks the args to see if a proto-based ime dump was requested and writes the client side 4361 * ime dump to the given {@link FileDescriptor}. 4362 * 4363 * @return {@code true} if a proto-based ime dump was requested. 4364 */ processDump(final FileDescriptor fd, final String[] args)4365 private boolean processDump(final FileDescriptor fd, final String[] args) { 4366 if (args == null) { 4367 return false; 4368 } 4369 4370 for (String arg : args) { 4371 if (arg.equals(ImeTracing.PROTO_ARG)) { 4372 final ProtoOutputStream proto = new ProtoOutputStream(fd); 4373 dumpDebug(proto, null /* icProto */); 4374 proto.flush(); 4375 return true; 4376 } 4377 } 4378 return false; 4379 } 4380 4381 /** 4382 * Write the proto dump of various client side components to the provided 4383 * {@link ProtoOutputStream}. 4384 * 4385 * @param proto The proto stream to which the dumps are written. 4386 * @param icProto {@link InputConnection} call data in proto format. 4387 * @hide 4388 */ dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto)4389 public void dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto) { 4390 synchronized (mH) { 4391 if (!isImeSessionAvailableLocked()) { 4392 return; 4393 } 4394 4395 proto.write(DISPLAY_ID, mDisplayId); 4396 final long token = proto.start(INPUT_METHOD_MANAGER); 4397 proto.write(CUR_ID, mCurBindState.mImeId); 4398 proto.write(FULLSCREEN_MODE, mFullscreenMode); 4399 proto.write(ACTIVE, mActive); 4400 proto.write(SERVED_CONNECTING, mServedConnecting); 4401 proto.write(SERVED_VIEW, Objects.toString(mServedView)); 4402 proto.write(NEXT_SERVED_VIEW, Objects.toString(mNextServedView)); 4403 proto.end(token); 4404 if (mCurRootView != null) { 4405 mCurRootView.dumpDebug(proto, VIEW_ROOT_IMPL); 4406 } 4407 if (mCurrentEditorInfo != null) { 4408 mCurrentEditorInfo.dumpDebug(proto, EDITOR_INFO); 4409 } 4410 if (mImeInsetsConsumer != null) { 4411 mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER); 4412 } 4413 if (mServedInputConnection != null) { 4414 mServedInputConnection.dumpDebug(proto, INPUT_CONNECTION); 4415 } 4416 if (icProto != null) { 4417 proto.write(INPUT_CONNECTION_CALL, icProto); 4418 } 4419 } 4420 } 4421 4422 @GuardedBy("mH") forAccessibilitySessionsLocked( Consumer<IAccessibilityInputMethodSessionInvoker> consumer)4423 private void forAccessibilitySessionsLocked( 4424 Consumer<IAccessibilityInputMethodSessionInvoker> consumer) { 4425 for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) { 4426 consumer.accept(mAccessibilityInputMethodSession.valueAt(i)); 4427 } 4428 } 4429 4430 @UiThread createInputConnection( @onNull View servedView)4431 private static Pair<InputConnection, EditorInfo> createInputConnection( 4432 @NonNull View servedView) { 4433 final EditorInfo editorInfo = new EditorInfo(); 4434 // Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the 4435 // system can verify the consistency between the uid of this process and package name passed 4436 // from here. See comment of Context#getOpPackageName() for details. 4437 editorInfo.packageName = servedView.getContext().getOpPackageName(); 4438 editorInfo.autofillId = servedView.getAutofillId(); 4439 editorInfo.fieldId = servedView.getId(); 4440 final InputConnection ic = servedView.onCreateInputConnection(editorInfo); 4441 if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic); 4442 4443 // Clear autofill and field ids if a connection could not be established. 4444 // This ensures that even disconnected EditorInfos have well-defined attributes, 4445 // making them consistently and straightforwardly comparable. 4446 if (ic == null) { 4447 editorInfo.autofillId = AutofillId.NO_AUTOFILL_ID; 4448 editorInfo.fieldId = 0; 4449 } 4450 return new Pair<>(ic, editorInfo); 4451 } 4452 } 4453