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