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