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