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