• 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.inputmethodservice;
18 
19 import static android.inputmethodservice.InputMethodServiceProto.CANDIDATES_VIEW_STARTED;
20 import static android.inputmethodservice.InputMethodServiceProto.CANDIDATES_VISIBILITY;
21 import static android.inputmethodservice.InputMethodServiceProto.CONFIGURATION;
22 import static android.inputmethodservice.InputMethodServiceProto.DECOR_VIEW_VISIBLE;
23 import static android.inputmethodservice.InputMethodServiceProto.DECOR_VIEW_WAS_VISIBLE;
24 import static android.inputmethodservice.InputMethodServiceProto.EXTRACTED_TOKEN;
25 import static android.inputmethodservice.InputMethodServiceProto.EXTRACT_VIEW_HIDDEN;
26 import static android.inputmethodservice.InputMethodServiceProto.FULLSCREEN_APPLIED;
27 import static android.inputmethodservice.InputMethodServiceProto.INPUT_BINDING;
28 import static android.inputmethodservice.InputMethodServiceProto.INPUT_CONNECTION_CALL;
29 import static android.inputmethodservice.InputMethodServiceProto.INPUT_EDITOR_INFO;
30 import static android.inputmethodservice.InputMethodServiceProto.INPUT_STARTED;
31 import static android.inputmethodservice.InputMethodServiceProto.INPUT_VIEW_STARTED;
32 import static android.inputmethodservice.InputMethodServiceProto.IN_SHOW_WINDOW;
33 import static android.inputmethodservice.InputMethodServiceProto.IS_FULLSCREEN;
34 import static android.inputmethodservice.InputMethodServiceProto.IS_INPUT_VIEW_SHOWN;
35 import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.CONTENT_TOP_INSETS;
36 import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.TOUCHABLE_INSETS;
37 import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.TOUCHABLE_REGION;
38 import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.VISIBLE_TOP_INSETS;
39 import static android.inputmethodservice.InputMethodServiceProto.LAST_COMPUTED_INSETS;
40 import static android.inputmethodservice.InputMethodServiceProto.LAST_SHOW_INPUT_REQUESTED;
41 import static android.inputmethodservice.InputMethodServiceProto.SETTINGS_OBSERVER;
42 import static android.inputmethodservice.InputMethodServiceProto.SHOW_INPUT_FLAGS;
43 import static android.inputmethodservice.InputMethodServiceProto.SHOW_INPUT_REQUESTED;
44 import static android.inputmethodservice.InputMethodServiceProto.SOFT_INPUT_WINDOW;
45 import static android.inputmethodservice.InputMethodServiceProto.STATUS_ICON;
46 import static android.inputmethodservice.InputMethodServiceProto.TOKEN;
47 import static android.inputmethodservice.InputMethodServiceProto.VIEWS_CREATED;
48 import static android.inputmethodservice.InputMethodServiceProto.WINDOW_VISIBLE;
49 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
50 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
51 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
52 import static android.view.WindowInsets.Type.navigationBars;
53 import static android.view.WindowInsets.Type.statusBars;
54 
55 import static java.lang.annotation.RetentionPolicy.SOURCE;
56 
57 import android.annotation.AnyThread;
58 import android.annotation.CallSuper;
59 import android.annotation.DrawableRes;
60 import android.annotation.IntDef;
61 import android.annotation.MainThread;
62 import android.annotation.NonNull;
63 import android.annotation.Nullable;
64 import android.annotation.TestApi;
65 import android.annotation.UiContext;
66 import android.app.ActivityManager;
67 import android.app.Dialog;
68 import android.compat.annotation.ChangeId;
69 import android.compat.annotation.EnabledSince;
70 import android.compat.annotation.UnsupportedAppUsage;
71 import android.content.Context;
72 import android.content.pm.PackageManager;
73 import android.content.res.Configuration;
74 import android.content.res.Resources;
75 import android.content.res.TypedArray;
76 import android.database.ContentObserver;
77 import android.graphics.Rect;
78 import android.graphics.Region;
79 import android.net.Uri;
80 import android.os.Binder;
81 import android.os.Build;
82 import android.os.Bundle;
83 import android.os.Handler;
84 import android.os.IBinder;
85 import android.os.Looper;
86 import android.os.Process;
87 import android.os.ResultReceiver;
88 import android.os.SystemClock;
89 import android.os.SystemProperties;
90 import android.os.Trace;
91 import android.provider.Settings;
92 import android.text.InputType;
93 import android.text.Layout;
94 import android.text.Spannable;
95 import android.text.method.MovementMethod;
96 import android.util.Log;
97 import android.util.PrintWriterPrinter;
98 import android.util.Printer;
99 import android.util.proto.ProtoOutputStream;
100 import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver;
101 import android.view.Choreographer;
102 import android.view.Gravity;
103 import android.view.InputChannel;
104 import android.view.InputDevice;
105 import android.view.InputEventReceiver;
106 import android.view.KeyCharacterMap;
107 import android.view.KeyEvent;
108 import android.view.LayoutInflater;
109 import android.view.MotionEvent;
110 import android.view.View;
111 import android.view.ViewGroup;
112 import android.view.ViewRootImpl;
113 import android.view.ViewTreeObserver;
114 import android.view.Window;
115 import android.view.WindowInsets.Side;
116 import android.view.WindowInsets.Type;
117 import android.view.WindowManager;
118 import android.view.animation.AnimationUtils;
119 import android.view.inputmethod.CompletionInfo;
120 import android.view.inputmethod.CursorAnchorInfo;
121 import android.view.inputmethod.EditorInfo;
122 import android.view.inputmethod.ExtractedText;
123 import android.view.inputmethod.ExtractedTextRequest;
124 import android.view.inputmethod.InlineSuggestionsRequest;
125 import android.view.inputmethod.InlineSuggestionsResponse;
126 import android.view.inputmethod.InputBinding;
127 import android.view.inputmethod.InputConnection;
128 import android.view.inputmethod.InputContentInfo;
129 import android.view.inputmethod.InputMethod;
130 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceProto;
131 import android.view.inputmethod.InputMethodInfo;
132 import android.view.inputmethod.InputMethodManager;
133 import android.view.inputmethod.InputMethodSubtype;
134 import android.widget.FrameLayout;
135 import android.widget.ImageButton;
136 import android.widget.LinearLayout;
137 import android.widget.TextView;
138 import android.window.CompatOnBackInvokedCallback;
139 import android.window.ImeOnBackInvokedDispatcher;
140 import android.window.OnBackInvokedCallback;
141 import android.window.OnBackInvokedDispatcher;
142 import android.window.WindowMetricsHelper;
143 
144 import com.android.internal.annotations.GuardedBy;
145 import com.android.internal.inputmethod.IInputContentUriToken;
146 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
147 import com.android.internal.inputmethod.ImeTracing;
148 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
149 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
150 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
151 import com.android.internal.inputmethod.SoftInputShowHideReason;
152 import com.android.internal.util.RingBuffer;
153 import com.android.internal.view.IInlineSuggestionsRequestCallback;
154 import com.android.internal.view.IInputContext;
155 import com.android.internal.view.InlineSuggestionsRequestInfo;
156 
157 import java.io.FileDescriptor;
158 import java.io.PrintWriter;
159 import java.lang.annotation.Retention;
160 import java.lang.annotation.RetentionPolicy;
161 import java.util.ArrayList;
162 import java.util.List;
163 import java.util.Objects;
164 import java.util.OptionalInt;
165 
166 /**
167  * InputMethodService provides a standard implementation of an InputMethod,
168  * which final implementations can derive from and customize.  See the
169  * base class {@link AbstractInputMethodService} and the {@link InputMethod}
170  * interface for more information on the basics of writing input methods.
171  *
172  * <p>In addition to the normal Service lifecycle methods, this class
173  * introduces some new specific callbacks that most subclasses will want
174  * to make use of:</p>
175  * <ul>
176  * <li> {@link #onInitializeInterface()} for user-interface initialization,
177  * in particular to deal with configuration changes while the service is
178  * running.
179  * <li> {@link #onBindInput} to find out about switching to a new client.
180  * <li> {@link #onStartInput} to deal with an input session starting with
181  * the client.
182  * <li> {@link #onCreateInputView()}, {@link #onCreateCandidatesView()},
183  * and {@link #onCreateExtractTextView()} for non-demand generation of the UI.
184  * <li> {@link #onStartInputView(EditorInfo, boolean)} to deal with input
185  * starting within the input area of the IME.
186  * </ul>
187  *
188  * <p>An input method has significant discretion in how it goes about its
189  * work: the {@link android.inputmethodservice.InputMethodService} provides
190  * a basic framework for standard UI elements (input view, candidates view,
191  * and running in fullscreen mode), but it is up to a particular implementor
192  * to decide how to use them.  For example, one input method could implement
193  * an input area with a keyboard, another could allow the user to draw text,
194  * while a third could have no input area (and thus not be visible to the
195  * user) but instead listen to audio and perform text to speech conversion.</p>
196  *
197  * <p>In the implementation provided here, all of these elements are placed
198  * together in a single window managed by the InputMethodService.  It will
199  * execute callbacks as it needs information about them, and provides APIs for
200  * programmatic control over them.  They layout of these elements is explicitly
201  * defined:</p>
202  *
203  * <ul>
204  * <li>The soft input view, if available, is placed at the bottom of the
205  * screen.
206  * <li>The candidates view, if currently shown, is placed above the soft
207  * input view.
208  * <li>If not running fullscreen, the application is moved or resized to be
209  * above these views; if running fullscreen, the window will completely cover
210  * the application and its top part will contain the extract text of what is
211  * currently being edited by the application.
212  * </ul>
213  *
214  *
215  * <a name="SoftInputView"></a>
216  * <h3>Soft Input View</h3>
217  *
218  * <p>Central to most input methods is the soft input view.  This is where most
219  * user interaction occurs: pressing on soft keys, drawing characters, or
220  * however else your input method wants to generate text.  Most implementations
221  * will simply have their own view doing all of this work, and return a new
222  * instance of it when {@link #onCreateInputView()} is called.  At that point,
223  * as long as the input view is visible, you will see user interaction in
224  * that view and can call back on the InputMethodService to interact with the
225  * application as appropriate.</p>
226  *
227  * <p>There are some situations where you want to decide whether or not your
228  * soft input view should be shown to the user.  This is done by implementing
229  * the {@link #onEvaluateInputViewShown()} to return true or false based on
230  * whether it should be shown in the current environment.  If any of your
231  * state has changed that may impact this, call
232  * {@link #updateInputViewShown()} to have it re-evaluated.  The default
233  * implementation always shows the input view unless there is a hard
234  * keyboard available, which is the appropriate behavior for most input
235  * methods.</p>
236  *
237  *
238  * <a name="CandidatesView"></a>
239  * <h3>Candidates View</h3>
240  *
241  * <p>Often while the user is generating raw text, an input method wants to
242  * provide them with a list of possible interpretations of that text that can
243  * be selected for use.  This is accomplished with the candidates view, and
244  * like the soft input view you implement {@link #onCreateCandidatesView()}
245  * to instantiate your own view implementing your candidates UI.</p>
246  *
247  * <p>Management of the candidates view is a little different than the input
248  * view, because the candidates view tends to be more transient, being shown
249  * only when there are possible candidates for the current text being entered
250  * by the user.  To control whether the candidates view is shown, you use
251  * {@link #setCandidatesViewShown(boolean)}.  Note that because the candidate
252  * view tends to be shown and hidden a lot, it does not impact the application
253  * UI in the same way as the soft input view: it will never cause application
254  * windows to resize, only cause them to be panned if needed for the user to
255  * see the current focus.</p>
256  *
257  *
258  * <a name="FullscreenMode"></a>
259  * <h3>Fullscreen Mode</h3>
260  *
261  * <p>Sometimes your input method UI is too large to integrate with the
262  * application UI, so you just want to take over the screen.  This is
263  * accomplished by switching to full-screen mode, causing the input method
264  * window to fill the entire screen and add its own "extracted text" editor
265  * showing the user the text that is being typed.  Unlike the other UI elements,
266  * there is a standard implementation for the extract editor that you should
267  * not need to change.  The editor is placed at the top of the IME, above the
268  * input and candidates views.</p>
269  *
270  * <p>Similar to the input view, you control whether the IME is running in
271  * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
272  * to return true or false based on
273  * whether it should be fullscreen in the current environment.  If any of your
274  * state has changed that may impact this, call
275  * {@link #updateFullscreenMode()} to have it re-evaluated.  The default
276  * implementation selects fullscreen mode when the screen is in a landscape
277  * orientation, which is appropriate behavior for most input methods that have
278  * a significant input area.</p>
279  *
280  * <p>When in fullscreen mode, you have some special requirements because the
281  * user can not see the application UI.  In particular, you should implement
282  * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
283  * generated by your application, typically in your candidates view like you
284  * would normally show candidates.
285  *
286  *
287  * <a name="GeneratingText"></a>
288  * <h3>Generating Text</h3>
289  *
290  * <p>The key part of an IME is of course generating text for the application.
291  * This is done through calls to the
292  * {@link android.view.inputmethod.InputConnection} interface to the
293  * application, which can be retrieved from {@link #getCurrentInputConnection()}.
294  * This interface allows you to generate raw key events or, if the target
295  * supports it, directly edit in strings of candidates and committed text.</p>
296  *
297  * <p>Information about what the target is expected and supports can be found
298  * through the {@link android.view.inputmethod.EditorInfo} class, which is
299  * retrieved with {@link #getCurrentInputEditorInfo()} method.  The most
300  * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
301  * EditorInfo.inputType}; in particular, if this is
302  * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
303  * then the target does not support complex edits and you need to only deliver
304  * raw key events to it.  An input method will also want to look at other
305  * values here, to for example detect password mode, auto complete text views,
306  * phone number entry, etc.</p>
307  *
308  * <p>When the user switches between input targets, you will receive calls to
309  * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
310  * You can use these to reset and initialize your input state for the current
311  * target.  For example, you will often want to clear any input state, and
312  * update a soft keyboard to be appropriate for the new inputType.</p>
313  *
314  * @attr ref android.R.styleable#InputMethodService_imeFullscreenBackground
315  * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation
316  * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation
317  */
318 @UiContext
319 public class InputMethodService extends AbstractInputMethodService {
320     static final String TAG = "InputMethodService";
321     static final boolean DEBUG = false;
322 
323     /**
324      * Key for a boolean value that tells whether {@link InputMethodService} is responsible for
325      * rendering the back button and the IME switcher button or not when the gestural navigation is
326      * enabled.
327      *
328      * <p>This sysprop is just ignored when the gestural navigation mode is not enabled.</p>
329      *
330      * <p>
331      * To avoid complexity that is not necessary for production, you always need to reboot the
332      * device after modifying this flag as follows:
333      * <pre>
334      * $ adb root
335      * $ adb shell setprop persist.sys.ime.can_render_gestural_nav_buttons true
336      * $ adb reboot
337      * </pre>
338      * </p>
339      */
340     private static final String PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS =
341             "persist.sys.ime.can_render_gestural_nav_buttons";
342 
343     /**
344      * Number of {@link MotionEvent} to buffer if IME is not ready with Ink view.
345      * This number may be configured eventually based on device's touch sampling frequency.
346      */
347     private static final int MAX_EVENTS_BUFFER = 500;
348 
349     /**
350      * A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
351      **/
352     private RingBuffer<MotionEvent> mPendingEvents;
353     private ImeOnBackInvokedDispatcher mImeDispatcher;
354     private Boolean mBackCallbackRegistered = false;
355     private final CompatOnBackInvokedCallback mCompatBackCallback = this::compatHandleBack;
356 
357     /**
358      * Returns whether {@link InputMethodService} is responsible for rendering the back button and
359      * the IME switcher button or not when the gestural navigation is enabled.
360      *
361      * <p>This method is supposed to be used with an assumption that the same value is returned in
362      * other processes. It is developers' responsibility for rebooting the device when the sysprop
363      * is modified.</p>
364      *
365      * @return {@code true} if {@link InputMethodService} is responsible for rendering the back
366      * button and the IME switcher button when the gestural navigation is enabled.
367      *
368      * @hide
369      */
370     @AnyThread
canImeRenderGesturalNavButtons()371     public static boolean canImeRenderGesturalNavButtons() {
372         return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true);
373     }
374 
375     /**
376      * Allows the system to optimize the back button affordance based on the presence of software
377      * keyboard.
378      *
379      * <p>For instance, on devices that have navigation bar and software-rendered back button, the
380      * system may use a different icon while {@link #isInputViewShown()} returns {@code true}, to
381      * indicate that the back button has "dismiss" affordance.</p>
382      *
383      * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
384      * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
385      * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
386      * not take this mode into account.</p>
387      *
388      * <p>For API level {@link android.os.Build.VERSION_CODES#O_MR1} and lower devices, this is the
389      * only mode you can safely specify without worrying about the compatibility.</p>
390      *
391      * @see #setBackDisposition(int)
392      */
393     public static final int BACK_DISPOSITION_DEFAULT = 0;
394 
395     /**
396      * Deprecated flag.
397      *
398      * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
399      *
400      * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
401      *             handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
402      *             {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
403      *             of this mode had not been well defined. Most likely the end result would be the
404      *             same as {@link #BACK_DISPOSITION_DEFAULT}. Either way it is not recommended to
405      *             use this mode
406      * @see #setBackDisposition(int)
407      */
408     @Deprecated
409     public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1;
410 
411     /**
412      * Deprecated flag.
413      *
414      * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
415      *
416      * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
417      *             handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
418      *             {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
419      *             of this mode had not been well defined. In AOSP implementation running on devices
420      *             that have navigation bar, specifying this flag could change the software back
421      *             button to "Dismiss" icon no matter whether the software keyboard is shown or not,
422      *             but there would be no easy way to restore the icon state even after IME lost the
423      *             connection to the application. To avoid user confusions, do not specify this mode
424      *             anyway
425      * @see #setBackDisposition(int)
426      */
427     @Deprecated
428     public static final int BACK_DISPOSITION_WILL_DISMISS = 2;
429 
430     /**
431      * Asks the system to not adjust the back button affordance even when the software keyboard is
432      * shown.
433      *
434      * <p>This mode is useful for UI modes where IME's main soft input window is used for some
435      * supplemental UI, such as floating candidate window for languages such as Chinese and
436      * Japanese, where users expect the back button is, or at least looks to be, handled by the
437      * target application rather than the UI shown by the IME even while {@link #isInputViewShown()}
438      * returns {@code true}.</p>
439      *
440      * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
441      * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
442      * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
443      * not take this mode into account.</p>
444      *
445      * @see #setBackDisposition(int)
446      */
447     public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
448 
449     /**
450      * Enum flag to be used for {@link #setBackDisposition(int)}.
451      *
452      * @hide
453      */
454     @Retention(SOURCE)
455     @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
456             BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
457             prefix = "BACK_DISPOSITION_")
458     public @interface BackDispositionMode {}
459 
460     /**
461      * @hide
462      * The IME is active.  It may or may not be visible.
463      */
464     public static final int IME_ACTIVE = 0x1;
465 
466     /**
467      * @hide
468      * The IME is perceptibly visible to the user.
469      */
470     public static final int IME_VISIBLE = 0x2;
471 
472     /**
473      * @hide
474      * The IME is active and ready with views but set invisible.
475      * This flag cannot be combined with {@link #IME_VISIBLE}.
476      */
477     public static final int IME_INVISIBLE = 0x4;
478 
479     /**
480      * @hide
481      * The IME is visible, but not yet perceptible to the user (e.g. fading in)
482      * by {@link android.view.WindowInsetsController}.
483      *
484      * @see InputMethodManager#reportPerceptible
485      */
486     public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x8;
487 
488     // Min and max values for back disposition.
489     private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
490     private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
491 
492     /**
493      * Timeout after which hidden IME surface will be removed from memory
494      * TODO(b/230762351): reset timeout to 5000ms and invalidate cache when IME insets change.
495      */
496     private static final long TIMEOUT_SURFACE_REMOVAL_MILLIS = 500;
497 
498     InputMethodManager mImm;
499     private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
500 
501     @NonNull
502     private final NavigationBarController mNavigationBarController =
503             new NavigationBarController(this);
504 
505     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
506     int mTheme = 0;
507 
508     /**
509      * Finish the {@link InputConnection} when the device becomes
510      * {@link android.os.PowerManager#isInteractive non-interactive}.
511      *
512      * <p>
513      * If enabled by the current {@link InputMethodService input method}, the current input
514      * connection will be {@link InputMethodService#onFinishInput finished} whenever the devices
515      * becomes non-interactive.
516      *
517      * <p>
518      * If not enabled, the current input connection will instead be silently deactivated when the
519      * devices becomes non-interactive, and an {@link InputMethodService#onFinishInput
520      * onFinishInput()} {@link InputMethodService#onStartInput onStartInput()} pair is dispatched
521      * when the device becomes interactive again.
522      *
523      * @hide
524      */
525     @TestApi
526     @ChangeId
527     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
528     public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // This is a bug id.
529 
530     LayoutInflater mInflater;
531     TypedArray mThemeAttrs;
532     @UnsupportedAppUsage
533     View mRootView;
534     SoftInputWindow mWindow;
535     boolean mInitialized;
536     boolean mViewsCreated;
537     // IME views visibility.
538     boolean mDecorViewVisible;
539     boolean mDecorViewWasVisible;
540     boolean mInShowWindow;
541     // IME window visibility.
542     // Use (mDecorViewVisible && mWindowVisible) to check if IME is visible to the user.
543     boolean mWindowVisible;
544 
545     ViewGroup mFullscreenArea;
546     FrameLayout mExtractFrame;
547     FrameLayout mCandidatesFrame;
548     FrameLayout mInputFrame;
549 
550     IBinder mToken;
551 
552     InputBinding mInputBinding;
553     InputConnection mInputConnection;
554     boolean mInputStarted;
555     boolean mInputViewStarted;
556     boolean mCandidatesViewStarted;
557     InputConnection mStartedInputConnection;
558     EditorInfo mInputEditorInfo;
559 
560     int mShowInputFlags;
561     boolean mShowInputRequested;
562     boolean mLastShowInputRequested;
563     int mCandidatesVisibility;
564     CompletionInfo[] mCurCompletions;
565 
566     boolean mFullscreenApplied;
567     boolean mIsFullscreen;
568     private boolean mLastWasInFullscreenMode;
569     @UnsupportedAppUsage
570     View mExtractView;
571     boolean mExtractViewHidden;
572     @UnsupportedAppUsage
573     ExtractEditText mExtractEditText;
574     ViewGroup mExtractAccessories;
575     View mExtractAction;
576     ExtractedText mExtractedText;
577     int mExtractedToken;
578 
579     View mInputView;
580     boolean mIsInputViewShown;
581 
582     int mStatusIcon;
583 
584     @BackDispositionMode
585     int mBackDisposition;
586 
587     private Object mLock = new Object();
588     @GuardedBy("mLock")
589     private boolean mNotifyUserActionSent;
590 
591     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
592     final Insets mTmpInsets = new Insets();
593     final int[] mTmpLocation = new int[2];
594 
595     private InlineSuggestionSessionController mInlineSuggestionSessionController;
596 
597     private boolean mHideNavBarForKeyboard;
598     private boolean mIsAutomotive;
599     private @NonNull OptionalInt mHandwritingRequestId = OptionalInt.empty();
600     private InputEventReceiver mHandwritingEventReceiver;
601     private Handler mHandler;
602     private boolean mImeSurfaceScheduledForRemoval;
603     private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker();
604     private boolean mDestroyed;
605     private boolean mOnPreparedStylusHwCalled;
606 
607     /** Stylus handwriting Ink window.  */
608     private InkWindow mInkWindow;
609 
610     /**
611      * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
612      * The original app window token is passed from client app window.
613      * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique
614      * placeholder token to identify this window.
615      * This placeholder token is only valid for a single call to
616      * {@link InputMethodImpl#showSoftInput}, after which it is set null until next call.
617      */
618     private IBinder mCurShowInputToken;
619 
620     /**
621      * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#hideSoftInput}
622      * The original app window token is passed from client app window.
623      * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique
624      * placeholder token to identify this window.
625      * This placeholder token is only valid for a single call to
626      * {@link InputMethodImpl#hideSoftInput}, after which it is set {@code null} until next call.
627      */
628     private IBinder mCurHideInputToken;
629 
630     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
631         onComputeInsets(mTmpInsets);
632         if (!mViewsCreated) {
633             // The IME views are not ready, keep visible insets untouched.
634             mTmpInsets.visibleTopInsets = 0;
635         }
636         if (isExtractViewShown()) {
637             // In true fullscreen mode, we just say the window isn't covering
638             // any content so we don't impact whatever is behind.
639             View decor = getWindow().getWindow().getDecorView();
640             info.contentInsets.top = info.visibleInsets.top = decor.getHeight();
641             info.touchableRegion.setEmpty();
642             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
643         } else {
644             info.contentInsets.top = mTmpInsets.contentTopInsets;
645             info.visibleInsets.top = mTmpInsets.visibleTopInsets;
646             info.touchableRegion.set(mTmpInsets.touchableRegion);
647             info.setTouchableInsets(mTmpInsets.touchableInsets);
648         }
649         mNavigationBarController.updateTouchableInsets(mTmpInsets, info);
650 
651         if (mInputFrame != null) {
652             setImeExclusionRect(mTmpInsets.visibleTopInsets);
653         }
654     };
655 
656     final View.OnClickListener mActionClickListener = v -> {
657         final EditorInfo ei = getCurrentInputEditorInfo();
658         final InputConnection ic = getCurrentInputConnection();
659         if (ei != null && ic != null) {
660             if (ei.actionId != 0) {
661                 ic.performEditorAction(ei.actionId);
662             } else if ((ei.imeOptions & EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE) {
663                 ic.performEditorAction(ei.imeOptions & EditorInfo.IME_MASK_ACTION);
664             }
665         }
666     };
667 
668     /**
669      * Concrete implementation of
670      * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
671      * all of the standard behavior for an input method.
672      */
673     public class InputMethodImpl extends AbstractInputMethodImpl {
674 
675         private boolean mSystemCallingShowSoftInput;
676         private boolean mSystemCallingHideSoftInput;
677 
678         /**
679          * {@inheritDoc}
680          * @hide
681          */
682         @MainThread
683         @Override
initializeInternal(@onNull IBinder token, IInputMethodPrivilegedOperations privilegedOperations, int configChanges, boolean stylusHwSupported, @InputMethodNavButtonFlags int navButtonFlags)684         public final void initializeInternal(@NonNull IBinder token,
685                 IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
686                 boolean stylusHwSupported, @InputMethodNavButtonFlags int navButtonFlags) {
687             if (mDestroyed) {
688                 Log.i(TAG, "The InputMethodService has already onDestroyed()."
689                     + "Ignore the initialization.");
690                 return;
691             }
692             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
693             mConfigTracker.onInitialize(configChanges);
694             mPrivOps.set(privilegedOperations);
695             InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
696             if (stylusHwSupported) {
697                 mInkWindow = new InkWindow(mWindow.getContext());
698             }
699             mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
700             attachToken(token);
701             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
702         }
703 
704         /**
705          * {@inheritDoc}
706          * @hide
707          */
708         @MainThread
709         @Override
onCreateInlineSuggestionsRequest( @onNull InlineSuggestionsRequestInfo requestInfo, @NonNull IInlineSuggestionsRequestCallback cb)710         public void onCreateInlineSuggestionsRequest(
711                 @NonNull InlineSuggestionsRequestInfo requestInfo,
712                 @NonNull IInlineSuggestionsRequestCallback cb) {
713             if (DEBUG) {
714                 Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
715             }
716             mInlineSuggestionSessionController.onMakeInlineSuggestionsRequest(requestInfo, cb);
717         }
718 
719         /**
720          * {@inheritDoc}
721          */
722         @MainThread
723         @Override
attachToken(IBinder token)724         public void attachToken(IBinder token) {
725             if (mToken != null) {
726                 throw new IllegalStateException(
727                         "attachToken() must be called at most once. token=" + token);
728             }
729             attachToWindowToken(token);
730             mToken = token;
731             mWindow.setToken(token);
732             if (mInkWindow != null) {
733                 mInkWindow.setToken(token);
734             }
735         }
736 
737         /**
738          * {@inheritDoc}
739          *
740          * <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
741          */
742         @MainThread
743         @Override
bindInput(InputBinding binding)744         public void bindInput(InputBinding binding) {
745             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.bindInput");
746             mInputBinding = binding;
747             mInputConnection = binding.getConnection();
748             if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
749                     + " ic=" + mInputConnection);
750             reportFullscreenMode();
751             initialize();
752             onBindInput();
753             mConfigTracker.onBindInput(getResources());
754             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
755         }
756 
757         /**
758          * {@inheritDoc}
759          *
760          * <p>Calls {@link InputMethodService#onUnbindInput()} when done.</p>
761          */
762         @MainThread
763         @Override
unbindInput()764         public void unbindInput() {
765             if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
766                     + " ic=" + mInputConnection);
767             // Unbind input is per process per display.
768             onUnbindInput();
769             mInputBinding = null;
770             mInputConnection = null;
771             // free-up cached InkWindow surface on detaching from current client.
772             if (mInkWindow != null) {
773                 mInkWindow.hide(true /* remove */);
774             }
775         }
776 
777         /**
778          * {@inheritDoc}
779          */
780         @MainThread
781         @Override
startInput(InputConnection ic, EditorInfo attribute)782         public void startInput(InputConnection ic, EditorInfo attribute) {
783             if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
784             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.startInput");
785             doStartInput(ic, attribute, false);
786             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
787         }
788 
789         /**
790          * {@inheritDoc}
791          */
792         @MainThread
793         @Override
restartInput(InputConnection ic, EditorInfo attribute)794         public void restartInput(InputConnection ic, EditorInfo attribute) {
795             if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
796             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.restartInput");
797             doStartInput(ic, attribute, true);
798             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
799         }
800 
801         /**
802          * {@inheritDoc}
803          * @hide
804          */
805         @MainThread
806         @Override
dispatchStartInputWithToken(@ullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags, @NonNull ImeOnBackInvokedDispatcher imeDispatcher)807         public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
808                 @NonNull EditorInfo editorInfo, boolean restarting,
809                 @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
810                 @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
811             mPrivOps.reportStartInputAsync(startInputToken);
812             mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
813             if (restarting) {
814                 restartInput(inputConnection, editorInfo);
815             } else {
816                 startInput(inputConnection, editorInfo);
817             }
818             // Update the IME dispatcher last, so that the previously registered back callback
819             // (if any) can be unregistered using the old dispatcher if {@link #doFinishInput()}
820             // is called from {@link #startInput(InputConnection, EditorInfo)} or
821             // {@link #restartInput(InputConnection, EditorInfo)}.
822             mImeDispatcher = imeDispatcher;
823             if (mWindow != null) {
824                 mWindow.getOnBackInvokedDispatcher().setImeOnBackInvokedDispatcher(
825                         imeDispatcher);
826             }
827         }
828 
829         /**
830          * {@inheritDoc}
831          * @hide
832          */
833         @MainThread
834         @Override
onNavButtonFlagsChanged(@nputMethodNavButtonFlags int navButtonFlags)835         public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
836             mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
837         }
838 
839         /**
840          * {@inheritDoc}
841          * @hide
842          */
843         @MainThread
844         @Override
hideSoftInputWithToken(int flags, ResultReceiver resultReceiver, IBinder hideInputToken)845         public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
846                 IBinder hideInputToken) {
847             mSystemCallingHideSoftInput = true;
848             mCurHideInputToken = hideInputToken;
849             hideSoftInput(flags, resultReceiver);
850             mCurHideInputToken = null;
851             mSystemCallingHideSoftInput = false;
852         }
853 
854         /**
855          * {@inheritDoc}
856          */
857         @MainThread
858         @Override
hideSoftInput(int flags, ResultReceiver resultReceiver)859         public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
860             if (DEBUG) Log.v(TAG, "hideSoftInput()");
861             if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
862                     && !mSystemCallingHideSoftInput) {
863                 Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
864                         + " Use requestHideSelf(int) itself");
865                 return;
866             }
867             ImeTracing.getInstance().triggerServiceDump(
868                     "InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
869                     null /* icProto */);
870             final boolean wasVisible = isInputViewShown();
871             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
872 
873             mShowInputFlags = 0;
874             mShowInputRequested = false;
875             hideWindow();
876             final boolean isVisible = isInputViewShown();
877             final boolean visibilityChanged = isVisible != wasVisible;
878             if (resultReceiver != null) {
879                 resultReceiver.send(visibilityChanged
880                         ? InputMethodManager.RESULT_HIDDEN
881                         : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
882                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
883             }
884             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
885         }
886 
887         /**
888          * {@inheritDoc}
889          * @hide
890          */
891         @MainThread
892         @Override
showSoftInputWithToken(int flags, ResultReceiver resultReceiver, IBinder showInputToken)893         public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
894                 IBinder showInputToken) {
895             mSystemCallingShowSoftInput = true;
896             mCurShowInputToken = showInputToken;
897             showSoftInput(flags, resultReceiver);
898             mCurShowInputToken = null;
899             mSystemCallingShowSoftInput = false;
900         }
901 
902         /**
903          * {@inheritDoc}
904          */
905         @MainThread
906         @Override
showSoftInput(int flags, ResultReceiver resultReceiver)907         public void showSoftInput(int flags, ResultReceiver resultReceiver) {
908             if (DEBUG) Log.v(TAG, "showSoftInput()");
909             // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
910             if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
911                     && !mSystemCallingShowSoftInput) {
912                 Log.e(TAG," IME shouldn't call showSoftInput on itself."
913                         + " Use requestShowSelf(int) itself");
914                 return;
915             }
916 
917             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
918             ImeTracing.getInstance().triggerServiceDump(
919                     "InputMethodService.InputMethodImpl#showSoftInput", mDumper,
920                     null /* icProto */);
921             final boolean wasVisible = isInputViewShown();
922             if (dispatchOnShowInputRequested(flags, false)) {
923                 showWindow(true);
924             }
925             setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
926 
927             final boolean isVisible = isInputViewShown();
928             final boolean visibilityChanged = isVisible != wasVisible;
929             if (resultReceiver != null) {
930                 resultReceiver.send(visibilityChanged
931                         ? InputMethodManager.RESULT_SHOWN
932                         : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
933                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
934             }
935             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
936         }
937 
938         /**
939          * {@inheritDoc}
940          * @hide
941          */
942         @Override
canStartStylusHandwriting(int requestId)943         public void canStartStylusHandwriting(int requestId) {
944             if (DEBUG) Log.v(TAG, "canStartStylusHandwriting()");
945             if (mHandwritingRequestId.isPresent()) {
946                 Log.d(TAG, "There is an ongoing Handwriting session. ignoring.");
947                 return;
948             }
949             if (!mInputStarted) {
950                 Log.d(TAG, "Input should have started before starting Stylus handwriting.");
951                 return;
952             }
953             if (!mOnPreparedStylusHwCalled) {
954                 // prepare hasn't been called by Stylus HOVER.
955                 onPrepareStylusHandwriting();
956                 mOnPreparedStylusHwCalled = true;
957             }
958             if (onStartStylusHandwriting()) {
959                 mPrivOps.onStylusHandwritingReady(requestId, Process.myPid());
960             } else {
961                 Log.i(TAG, "IME is not ready. Can't start Stylus Handwriting");
962                 // TODO(b/210039666): see if it's valuable to propagate this back to IMM.
963             }
964         }
965 
966         /**
967          * {@inheritDoc}
968          * @hide
969          */
970         @MainThread
971         @Override
startStylusHandwriting( int requestId, @NonNull InputChannel channel, @NonNull List<MotionEvent> stylusEvents)972         public void startStylusHandwriting(
973                 int requestId, @NonNull InputChannel channel,
974                 @NonNull List<MotionEvent> stylusEvents) {
975             if (DEBUG) Log.v(TAG, "startStylusHandwriting()");
976             Objects.requireNonNull(channel);
977             Objects.requireNonNull(stylusEvents);
978 
979             if (mHandwritingRequestId.isPresent()) {
980                 return;
981             }
982 
983             mHandwritingRequestId = OptionalInt.of(requestId);
984             mShowInputRequested = false;
985 
986             mInkWindow.show();
987 
988             // deliver previous @param stylusEvents
989             stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent);
990 
991             // create receiver for channel
992             mHandwritingEventReceiver = new SimpleBatchedInputEventReceiver(
993                     channel,
994                     Looper.getMainLooper(), Choreographer.getInstance(),
995                     event -> {
996                         if (!(event instanceof MotionEvent)) {
997                             return false;
998                         }
999                         onStylusHandwritingMotionEvent((MotionEvent) event);
1000                         return true;
1001                     });
1002         }
1003 
1004         /**
1005          * {@inheritDoc}
1006          * @hide
1007          */
1008         @Override
initInkWindow()1009         public void initInkWindow() {
1010             mInkWindow.initOnly();
1011             onPrepareStylusHandwriting();
1012             mOnPreparedStylusHwCalled = true;
1013         }
1014 
1015         /**
1016          * {@inheritDoc}
1017          * @hide
1018          */
1019         @Override
finishStylusHandwriting()1020         public void finishStylusHandwriting() {
1021             InputMethodService.this.finishStylusHandwriting();
1022         }
1023 
1024         /**
1025          * {@inheritDoc}
1026          */
1027         @MainThread
1028         @Override
changeInputMethodSubtype(InputMethodSubtype subtype)1029         public void changeInputMethodSubtype(InputMethodSubtype subtype) {
1030             dispatchOnCurrentInputMethodSubtypeChanged(subtype);
1031         }
1032     }
1033 
1034     /**
1035      * Called when Autofill is requesting an {@link InlineSuggestionsRequest} from the IME.
1036      *
1037      * <p>The Autofill Framework will first request the IME to create and send an
1038      * {@link InlineSuggestionsRequest} back. Once Autofill Framework receives a valid request and
1039      * also receives valid inline suggestions, they will be returned via
1040      * {@link #onInlineSuggestionsResponse(InlineSuggestionsResponse)}.</p>
1041      *
1042      * <p>IME Lifecycle - The request will wait to be created after inputStarted</p>
1043      *
1044      * <p>If the IME wants to support displaying inline suggestions, they must set
1045      * supportsInlineSuggestions in its XML and implement this method to return a valid
1046      * {@link InlineSuggestionsRequest}.</p>
1047      *
1048      * @param uiExtras the extras that contain the UI renderer related information
1049      * @return an {@link InlineSuggestionsRequest} to be sent to Autofill.
1050      */
1051     @Nullable
onCreateInlineSuggestionsRequest(@onNull Bundle uiExtras)1052     public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull Bundle uiExtras) {
1053         return null;
1054     }
1055 
1056     /**
1057      * Called when Autofill responds back with {@link InlineSuggestionsResponse} containing
1058      * inline suggestions.
1059      *
1060      * <p>Should be implemented by subclasses.</p>
1061      *
1062      * @param response {@link InlineSuggestionsResponse} passed back by Autofill.
1063      * @return Whether the IME will use and render  the inline suggestions.
1064      */
onInlineSuggestionsResponse(@onNull InlineSuggestionsResponse response)1065     public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
1066         return false;
1067     }
1068 
1069     /**
1070      * Returns the {@link IBinder} input token from the host view root.
1071      */
1072     @Nullable
getHostInputToken()1073     private IBinder getHostInputToken() {
1074         ViewRootImpl viewRoot = null;
1075         if (mRootView != null) {
1076             viewRoot = mRootView.getViewRootImpl();
1077         }
1078         return viewRoot == null ? null : viewRoot.getInputToken();
1079     }
1080 
scheduleImeSurfaceRemoval()1081     private void scheduleImeSurfaceRemoval() {
1082         if (mShowInputRequested || mWindowVisible || mWindow == null
1083                 || mImeSurfaceScheduledForRemoval) {
1084             return;
1085         }
1086         if (mHandler == null) {
1087             mHandler = new Handler(getMainLooper());
1088         }
1089 
1090         if (mLastWasInFullscreenMode) {
1091             // Caching surface / delaying surface removal can cause mServedView to detach in certain
1092             // cases in RecyclerView (b/187772544).
1093             // TODO(b/188818557): Re-enable IME surface caching for fullscreen mode once detaching
1094             //  view issues is resolved in RecyclerView.
1095             removeImeSurface();
1096         } else {
1097             mImeSurfaceScheduledForRemoval = true;
1098             mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS);
1099         }
1100     }
1101 
removeImeSurface()1102     private void removeImeSurface() {
1103         // hiding a window removes its surface.
1104         if (mWindow != null) {
1105             mWindow.hide();
1106         }
1107         mImeSurfaceScheduledForRemoval = false;
1108     }
1109 
cancelImeSurfaceRemoval()1110     private void cancelImeSurfaceRemoval() {
1111         if (mHandler != null && mImeSurfaceScheduledForRemoval) {
1112             mHandler.removeCallbacksAndMessages(null /* token */);
1113             mImeSurfaceScheduledForRemoval = false;
1114         }
1115     }
1116 
setImeWindowStatus(int visibilityFlags, int backDisposition)1117     private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
1118         mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
1119     }
1120 
1121     /** Set region of the keyboard to be avoided from back gesture */
setImeExclusionRect(int visibleTopInsets)1122     private void setImeExclusionRect(int visibleTopInsets) {
1123         View rootView = mInputFrame.getRootView();
1124         android.graphics.Insets systemGesture =
1125                 rootView.getRootWindowInsets().getInsets(Type.systemGestures());
1126         ArrayList<Rect> exclusionRects = new ArrayList<>();
1127         exclusionRects.add(new Rect(0,
1128                 visibleTopInsets,
1129                 systemGesture.left,
1130                 rootView.getHeight()));
1131         exclusionRects.add(new Rect(rootView.getWidth() - systemGesture.right,
1132                 visibleTopInsets,
1133                 rootView.getWidth(),
1134                 rootView.getHeight()));
1135         rootView.setSystemGestureExclusionRects(exclusionRects);
1136     }
1137 
1138     /**
1139      * Concrete implementation of
1140      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
1141      * all of the standard behavior for an input method session.
1142      */
1143     public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
finishInput()1144         public void finishInput() {
1145             if (!isEnabled()) {
1146                 return;
1147             }
1148             if (DEBUG) Log.v(TAG, "finishInput() in " + this);
1149             doFinishInput();
1150         }
1151 
1152         /**
1153          * Call {@link InputMethodService#onDisplayCompletions
1154          * InputMethodService.onDisplayCompletions()}.
1155          */
displayCompletions(CompletionInfo[] completions)1156         public void displayCompletions(CompletionInfo[] completions) {
1157             if (!isEnabled()) {
1158                 return;
1159             }
1160             mCurCompletions = completions;
1161             onDisplayCompletions(completions);
1162         }
1163 
1164         /**
1165          * Call {@link InputMethodService#onUpdateExtractedText
1166          * InputMethodService.onUpdateExtractedText()}.
1167          */
updateExtractedText(int token, ExtractedText text)1168         public void updateExtractedText(int token, ExtractedText text) {
1169             if (!isEnabled()) {
1170                 return;
1171             }
1172             onUpdateExtractedText(token, text);
1173         }
1174 
1175         /**
1176          * Call {@link InputMethodService#onUpdateSelection
1177          * InputMethodService.onUpdateSelection()}.
1178          */
updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)1179         public void updateSelection(int oldSelStart, int oldSelEnd,
1180                 int newSelStart, int newSelEnd,
1181                 int candidatesStart, int candidatesEnd) {
1182             if (!isEnabled()) {
1183                 return;
1184             }
1185             InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
1186                     newSelStart, newSelEnd, candidatesStart, candidatesEnd);
1187         }
1188 
1189         @Override
viewClicked(boolean focusChanged)1190         public void viewClicked(boolean focusChanged) {
1191             if (!isEnabled()) {
1192                 return;
1193             }
1194             InputMethodService.this.onViewClicked(focusChanged);
1195         }
1196 
1197         /**
1198          * Call {@link InputMethodService#onUpdateCursor
1199          * InputMethodService.onUpdateCursor()}.
1200          */
updateCursor(Rect newCursor)1201         public void updateCursor(Rect newCursor) {
1202             if (!isEnabled()) {
1203                 return;
1204             }
1205             InputMethodService.this.onUpdateCursor(newCursor);
1206         }
1207 
1208         /**
1209          * Call {@link InputMethodService#onAppPrivateCommand
1210          * InputMethodService.onAppPrivateCommand()}.
1211          */
appPrivateCommand(String action, Bundle data)1212         public void appPrivateCommand(String action, Bundle data) {
1213             if (!isEnabled()) {
1214                 return;
1215             }
1216             InputMethodService.this.onAppPrivateCommand(action, data);
1217         }
1218 
1219         /**
1220          * Handles a request to toggle the IME visibility.
1221          *
1222          * @deprecated Starting in {@link Build.VERSION_CODES#S} the system no longer invokes this
1223          * method, instead it explicitly shows or hides the IME. An {@code InputMethodService}
1224          * wishing to toggle its own visibility should instead invoke {@link
1225          * InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
1226          */
1227         @Deprecated
toggleSoftInput(int showFlags, int hideFlags)1228         public void toggleSoftInput(int showFlags, int hideFlags) {
1229             InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
1230         }
1231 
1232         /**
1233          * Call {@link InputMethodService#onUpdateCursorAnchorInfo
1234          * InputMethodService.onUpdateCursorAnchorInfo()}.
1235          */
updateCursorAnchorInfo(CursorAnchorInfo info)1236         public void updateCursorAnchorInfo(CursorAnchorInfo info) {
1237             if (!isEnabled()) {
1238                 return;
1239             }
1240             InputMethodService.this.onUpdateCursorAnchorInfo(info);
1241         }
1242 
1243         /**
1244          * Notify IME that surface can be now removed.
1245          * @hide
1246          */
removeImeSurface()1247         public final void removeImeSurface() {
1248             InputMethodService.this.scheduleImeSurfaceRemoval();
1249         }
1250 
1251         /**
1252          * {@inheritDoc}
1253          * @hide
1254          */
1255         @Override
invalidateInputInternal(@onNull EditorInfo editorInfo, @NonNull IInputContext inputContext, int sessionId)1256         public final void invalidateInputInternal(@NonNull EditorInfo editorInfo,
1257                 @NonNull IInputContext inputContext, int sessionId) {
1258             if (mStartedInputConnection instanceof RemoteInputConnection) {
1259                 final RemoteInputConnection ric = (RemoteInputConnection) mStartedInputConnection;
1260                 if (!ric.isSameConnection(inputContext)) {
1261                     // This is not an error, and can be safely ignored.
1262                     if (DEBUG) {
1263                         Log.d(TAG, "ignoring invalidateInput() due to context mismatch.");
1264                     }
1265                     return;
1266                 }
1267                 editorInfo.makeCompatible(getApplicationInfo().targetSdkVersion);
1268                 getInputMethodInternal().restartInput(new RemoteInputConnection(ric, sessionId),
1269                         editorInfo);
1270             }
1271         }
1272     }
1273 
1274     /**
1275      * Information about where interesting parts of the input method UI appear.
1276      */
1277     public static final class Insets {
1278         /**
1279          * This is the top part of the UI that is the main content.  It is
1280          * used to determine the basic space needed, to resize/pan the
1281          * application behind.  It is assumed that this inset does not
1282          * change very much, since any change will cause a full resize/pan
1283          * of the application behind.  This value is relative to the top edge
1284          * of the input method window.
1285          */
1286         public int contentTopInsets;
1287 
1288         /**
1289          * This is the top part of the UI that is visibly covering the
1290          * application behind it.  This provides finer-grained control over
1291          * visibility, allowing you to change it relatively frequently (such
1292          * as hiding or showing candidates) without disrupting the underlying
1293          * UI too much.  For example, this will never resize the application
1294          * UI, will only pan if needed to make the current focus visible, and
1295          * will not aggressively move the pan position when this changes unless
1296          * needed to make the focus visible.  This value is relative to the top edge
1297          * of the input method window.
1298          */
1299         public int visibleTopInsets;
1300 
1301         /**
1302          * This is the region of the UI that is touchable.  It is used when
1303          * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
1304          * The region should be specified relative to the origin of the window frame.
1305          */
1306         public final Region touchableRegion = new Region();
1307 
1308         /**
1309          * Option for {@link #touchableInsets}: the entire window frame
1310          * can be touched.
1311          */
1312         public static final int TOUCHABLE_INSETS_FRAME
1313                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
1314 
1315         /**
1316          * Option for {@link #touchableInsets}: the area inside of
1317          * the content insets can be touched.
1318          */
1319         public static final int TOUCHABLE_INSETS_CONTENT
1320                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
1321 
1322         /**
1323          * Option for {@link #touchableInsets}: the area inside of
1324          * the visible insets can be touched.
1325          */
1326         public static final int TOUCHABLE_INSETS_VISIBLE
1327                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
1328 
1329         /**
1330          * Option for {@link #touchableInsets}: the region specified by
1331          * {@link #touchableRegion} can be touched.
1332          */
1333         public static final int TOUCHABLE_INSETS_REGION
1334                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
1335 
1336         /**
1337          * Determine which area of the window is touchable by the user.  May
1338          * be one of: {@link #TOUCHABLE_INSETS_FRAME},
1339          * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
1340          * or {@link #TOUCHABLE_INSETS_REGION}.
1341          */
1342         public int touchableInsets;
1343 
dumpDebug(ProtoOutputStream proto, long fieldId)1344         private void dumpDebug(ProtoOutputStream proto, long fieldId) {
1345             final long token = proto.start(fieldId);
1346             proto.write(CONTENT_TOP_INSETS, contentTopInsets);
1347             proto.write(VISIBLE_TOP_INSETS, visibleTopInsets);
1348             proto.write(TOUCHABLE_INSETS, touchableInsets);
1349             proto.write(TOUCHABLE_REGION, touchableRegion.toString());
1350             proto.end(token);
1351         }
1352     }
1353 
1354     /**
1355      * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
1356      *
1357      * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
1358      * Basically this functionality still needs to be considered as implementation details.</p>
1359      */
1360     @MainThread
1361     private static final class SettingsObserver extends ContentObserver {
1362         @Retention(RetentionPolicy.SOURCE)
1363         @IntDef({
1364                 ShowImeWithHardKeyboardType.UNKNOWN,
1365                 ShowImeWithHardKeyboardType.FALSE,
1366                 ShowImeWithHardKeyboardType.TRUE,
1367         })
1368         private @interface ShowImeWithHardKeyboardType {
1369             int UNKNOWN = 0;
1370             int FALSE = 1;
1371             int TRUE = 2;
1372         }
1373         @ShowImeWithHardKeyboardType
1374         private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
1375 
1376         private final InputMethodService mService;
1377 
SettingsObserver(InputMethodService service)1378         private SettingsObserver(InputMethodService service) {
1379             super(new Handler(service.getMainLooper()));
1380             mService = service;
1381         }
1382 
1383         /**
1384          * A factory method that internally enforces two-phase initialization to make sure that the
1385          * object reference will not be escaped until the object is properly constructed.
1386          *
1387          * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread.  Hence
1388          * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
1389          *
1390          * @param service {@link InputMethodService} that needs to receive the callback.
1391          * @return {@link SettingsObserver} that is already registered to
1392          * {@link android.content.ContentResolver}. The caller must call
1393          * {@link SettingsObserver#unregister()}.
1394          */
createAndRegister(InputMethodService service)1395         public static SettingsObserver createAndRegister(InputMethodService service) {
1396             final SettingsObserver observer = new SettingsObserver(service);
1397             // The observer is properly constructed. Let's start accepting the event.
1398             service.getContentResolver().registerContentObserver(
1399                     Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
1400                     false, observer);
1401             return observer;
1402         }
1403 
unregister()1404         void unregister() {
1405             mService.getContentResolver().unregisterContentObserver(this);
1406         }
1407 
1408         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
shouldShowImeWithHardKeyboard()1409         private boolean shouldShowImeWithHardKeyboard() {
1410             // Lazily initialize as needed.
1411             if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
1412                 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1413                         Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1414                         ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
1415             }
1416             switch (mShowImeWithHardKeyboard) {
1417                 case ShowImeWithHardKeyboardType.TRUE:
1418                     return true;
1419                 case ShowImeWithHardKeyboardType.FALSE:
1420                     return false;
1421                 default:
1422                     Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
1423                     return false;
1424             }
1425         }
1426 
1427         @Override
onChange(boolean selfChange, Uri uri)1428         public void onChange(boolean selfChange, Uri uri) {
1429             final Uri showImeWithHardKeyboardUri =
1430                     Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
1431             if (showImeWithHardKeyboardUri.equals(uri)) {
1432                 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
1433                         Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
1434                         ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
1435                 // In Android M and prior, state change of
1436                 // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
1437                 // #onConfigurationChanged().  For compatibility reasons, we reset the internal
1438                 // state as if configuration was changed.
1439                 mService.resetStateForNewConfiguration();
1440             }
1441         }
1442 
1443         @Override
toString()1444         public String toString() {
1445             return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard  + "}";
1446         }
1447     }
1448     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1449     private SettingsObserver mSettingsObserver;
1450 
1451     /**
1452      * You can call this to customize the theme used by your IME's window.
1453      * This theme should typically be one that derives from
1454      * {@link android.R.style#Theme_InputMethod}, which is the default theme
1455      * you will get.  This must be set before {@link #onCreate}, so you
1456      * will typically call it in your constructor with the resource ID
1457      * of your custom theme.
1458      */
1459     @Override
setTheme(int theme)1460     public void setTheme(int theme) {
1461         if (mWindow != null) {
1462             throw new IllegalStateException("Must be called before onCreate()");
1463         }
1464         mTheme = theme;
1465     }
1466 
1467     /**
1468      * You can call this to try to enable accelerated drawing for your IME. This must be set before
1469      * {@link #onCreate()}, so you will typically call it in your constructor.  It is not always
1470      * possible to use hardware accelerated drawing in an IME (for example on low-end devices that
1471      * do not have the resources to support this), so the call {@code true} if it succeeds otherwise
1472      * {@code false} if you will need to draw in software.  You must be able to handle either case.
1473      *
1474      * <p>In API 21 and later, system may automatically enable hardware accelerated drawing for your
1475      * IME on capable devices even if this method is not explicitly called. Make sure that your IME
1476      * is able to handle either case.</p>
1477      *
1478      * @return {@code true} if accelerated drawing is successfully enabled otherwise {@code false}.
1479      *         On API 21 and later devices the return value is basically just a hint and your IME
1480      *         does not need to change the behavior based on the it
1481      * @deprecated Starting in API 21, hardware acceleration is always enabled on capable devices
1482      */
1483     @Deprecated
enableHardwareAcceleration()1484     public boolean enableHardwareAcceleration() {
1485         if (mWindow != null) {
1486             throw new IllegalStateException("Must be called before onCreate()");
1487         }
1488         return ActivityManager.isHighEndGfx();
1489     }
1490 
onCreate()1491     @Override public void onCreate() {
1492         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate");
1493         mTheme = Resources.selectSystemTheme(mTheme,
1494                 getApplicationInfo().targetSdkVersion,
1495                 android.R.style.Theme_InputMethod,
1496                 android.R.style.Theme_Holo_InputMethod,
1497                 android.R.style.Theme_DeviceDefault_InputMethod,
1498                 android.R.style.Theme_DeviceDefault_InputMethod);
1499         super.setTheme(mTheme);
1500         super.onCreate();
1501         mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
1502         mSettingsObserver = SettingsObserver.createAndRegister(this);
1503         // cache preference so we don't have to read ContentProvider when IME is requested to be
1504         // shown the first time (cold start).
1505         mSettingsObserver.shouldShowImeWithHardKeyboard();
1506 
1507         mHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
1508                 com.android.internal.R.bool.config_hideNavBarForKeyboard);
1509 
1510         // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
1511         // for update resources & configuration correctly when show soft input
1512         // in non-default display.
1513         mInflater = (LayoutInflater)getSystemService(
1514                 Context.LAYOUT_INFLATER_SERVICE);
1515         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
1516         mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
1517         if (mImeDispatcher != null) {
1518             mWindow.getOnBackInvokedDispatcher()
1519                     .setImeOnBackInvokedDispatcher(mImeDispatcher);
1520         }
1521         mNavigationBarController.onSoftInputWindowCreated(mWindow);
1522         {
1523             final Window window = mWindow.getWindow();
1524             {
1525                 final WindowManager.LayoutParams lp = window.getAttributes();
1526                 lp.setTitle("InputMethod");
1527                 lp.type = WindowManager.LayoutParams.TYPE_INPUT_METHOD;
1528                 lp.width = WindowManager.LayoutParams.MATCH_PARENT;
1529                 lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
1530                 lp.gravity = Gravity.BOTTOM;
1531                 lp.setFitInsetsTypes(statusBars() | navigationBars());
1532                 lp.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
1533                 lp.receiveInsetsIgnoringZOrder = true;
1534                 window.setAttributes(lp);
1535             }
1536 
1537             // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
1538             // by default (but IME developers can opt this out later if they want a new behavior).
1539             final int windowFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1540                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1541                     | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
1542             final int windowFlagsMask = windowFlags
1543                     | WindowManager.LayoutParams.FLAG_DIM_BEHIND;  // to be unset
1544             window.setFlags(windowFlags, windowFlagsMask);
1545 
1546             // Automotive devices may request the navigation bar to be hidden when the IME shows up
1547             // (controlled via config_hideNavBarForKeyboard) in order to maximize the visible
1548             // screen real estate. When this happens, the IME window should animate from the
1549             // bottom of the screen to reduce the jank that happens from the lack of synchronization
1550             // between the bottom system window and the IME window.
1551             if (mHideNavBarForKeyboard) {
1552                 window.setDecorFitsSystemWindows(false);
1553             }
1554         }
1555 
1556         initViews();
1557         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
1558 
1559         mInlineSuggestionSessionController = new InlineSuggestionSessionController(
1560                 this::onCreateInlineSuggestionsRequest, this::getHostInputToken,
1561                 this::onInlineSuggestionsResponse);
1562         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
1563     }
1564 
1565     /**
1566      * This is a hook that subclasses can use to perform initialization of
1567      * their interface.  It is called for you prior to any of your UI objects
1568      * being created, both after the service is first created and after a
1569      * configuration change happens.
1570      */
onInitializeInterface()1571     public void onInitializeInterface() {
1572         // Intentionally empty
1573     }
1574 
initialize()1575     void initialize() {
1576         if (!mInitialized) {
1577             mInitialized = true;
1578             onInitializeInterface();
1579         }
1580     }
1581 
initViews()1582     void initViews() {
1583         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initViews");
1584         mInitialized = false;
1585         mViewsCreated = false;
1586         mShowInputRequested = false;
1587         mShowInputFlags = 0;
1588 
1589         mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
1590         mRootView = mInflater.inflate(
1591                 com.android.internal.R.layout.input_method, null);
1592         mWindow.setContentView(mRootView);
1593         mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
1594         mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
1595         mExtractViewHidden = false;
1596         mExtractFrame = mRootView.findViewById(android.R.id.extractArea);
1597         mExtractView = null;
1598         mExtractEditText = null;
1599         mExtractAccessories = null;
1600         mExtractAction = null;
1601         mFullscreenApplied = false;
1602 
1603         mCandidatesFrame = mRootView.findViewById(android.R.id.candidatesArea);
1604         mInputFrame = mRootView.findViewById(android.R.id.inputArea);
1605         mInputView = null;
1606         mIsInputViewShown = false;
1607 
1608         mExtractFrame.setVisibility(View.GONE);
1609         mCandidatesVisibility = getCandidatesHiddenVisibility();
1610         mCandidatesFrame.setVisibility(mCandidatesVisibility);
1611         mInputFrame.setVisibility(View.GONE);
1612         mNavigationBarController.onViewInitialized();
1613         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
1614     }
1615 
onDestroy()1616     @Override public void onDestroy() {
1617         mDestroyed = true;
1618         super.onDestroy();
1619         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1620                 mInsetsComputer);
1621         doFinishInput();
1622         mNavigationBarController.onDestroy();
1623         mWindow.dismissForDestroyIfNecessary();
1624         if (mSettingsObserver != null) {
1625             mSettingsObserver.unregister();
1626             mSettingsObserver = null;
1627         }
1628         if (mToken != null) {
1629             // This is completely optional, but allows us to show more explicit error messages
1630             // when IME developers are doing something unsupported.
1631             InputMethodPrivilegedOperationsRegistry.remove(mToken);
1632         }
1633         mImeDispatcher = null;
1634     }
1635 
1636     /**
1637      * Take care of handling configuration changes.  Subclasses of
1638      * InputMethodService generally don't need to deal directly with
1639      * this on their own; the standard implementation here takes care of
1640      * regenerating the input method UI as a result of the configuration
1641      * change, so you can rely on your {@link #onCreateInputView} and
1642      * other methods being called as appropriate due to a configuration change.
1643      *
1644      * <p>When a configuration change does happen,
1645      * {@link #onInitializeInterface()} is guaranteed to be called the next
1646      * time prior to any of the other input or UI creation callbacks.  The
1647      * following will be called immediately depending if appropriate for current
1648      * state: {@link #onStartInput} if input is active, and
1649      * {@link #onCreateInputView} and {@link #onStartInputView} and related
1650      * appropriate functions if the UI is displayed.
1651      * <p>Starting with {@link Build.VERSION_CODES#S}, IMEs can opt into handling configuration
1652      * changes themselves instead of being restarted with
1653      * {@link android.R.styleable#InputMethod_configChanges}.
1654      */
onConfigurationChanged(Configuration newConfig)1655     @Override public void onConfigurationChanged(Configuration newConfig) {
1656         super.onConfigurationChanged(newConfig);
1657         mConfigTracker.onConfigurationChanged(newConfig, this::resetStateForNewConfiguration);
1658     }
1659 
resetStateForNewConfiguration()1660     private void resetStateForNewConfiguration() {
1661         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.resetStateForNewConfiguration");
1662         boolean visible = mDecorViewVisible;
1663         int showFlags = mShowInputFlags;
1664         boolean showingInput = mShowInputRequested;
1665         CompletionInfo[] completions = mCurCompletions;
1666         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
1667         initViews();
1668         mInputViewStarted = false;
1669         mCandidatesViewStarted = false;
1670         if (mInputStarted) {
1671             doStartInput(getCurrentInputConnection(),
1672                     getCurrentInputEditorInfo(), true);
1673         }
1674         if (visible) {
1675             if (showingInput) {
1676                 // If we were last showing the soft keyboard, try to do so again.
1677                 if (dispatchOnShowInputRequested(showFlags, true)) {
1678                     showWindow(true);
1679                     if (completions != null) {
1680                         mCurCompletions = completions;
1681                         onDisplayCompletions(completions);
1682                     }
1683                 } else {
1684                     hideWindow();
1685                 }
1686             } else if (mCandidatesVisibility == View.VISIBLE) {
1687                 // If the candidates are currently visible, make sure the
1688                 // window is shown for them.
1689                 showWindow(false);
1690             } else {
1691                 // Otherwise hide the window.
1692                 hideWindow();
1693             }
1694             // If user uses hard keyboard, IME button should always be shown.
1695             boolean showing = onEvaluateInputViewShown();
1696             setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
1697         }
1698         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
1699     }
1700 
1701     /**
1702      * Implement to return our standard {@link InputMethodImpl}.  Subclasses
1703      * can override to provide their own customized version.
1704      */
1705     @Override
onCreateInputMethodInterface()1706     public AbstractInputMethodImpl onCreateInputMethodInterface() {
1707         return new InputMethodImpl();
1708     }
1709 
1710     /**
1711      * Implement to return our standard {@link InputMethodSessionImpl}.  Subclasses
1712      * can override to provide their own customized version.
1713      */
1714     @Override
onCreateInputMethodSessionInterface()1715     public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
1716         return new InputMethodSessionImpl();
1717     }
1718 
getLayoutInflater()1719     public LayoutInflater getLayoutInflater() {
1720         return mInflater;
1721     }
1722 
getWindow()1723     public Dialog getWindow() {
1724         return mWindow;
1725     }
1726 
1727     /**
1728      * Sets the disposition mode that indicates the expected affordance for the back button.
1729      *
1730      * <p>Keep in mind that specifying this flag does not change the the default behavior of
1731      * {@link #onKeyDown(int, KeyEvent)}.  It is IME developers' responsibility for making sure that
1732      * their custom implementation of {@link #onKeyDown(int, KeyEvent)} is consistent with the mode
1733      * specified to this API.</p>
1734      *
1735      * @see #getBackDisposition()
1736      * @param disposition disposition mode to be set
1737      */
setBackDisposition(@ackDispositionMode int disposition)1738     public void setBackDisposition(@BackDispositionMode int disposition) {
1739         if (disposition == mBackDisposition) {
1740             return;
1741         }
1742         if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
1743             Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
1744             return;
1745         }
1746         mBackDisposition = disposition;
1747         setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
1748     }
1749 
1750     /**
1751      * Retrieves the current disposition mode that indicates the expected back button affordance.
1752      *
1753      * @see #setBackDisposition(int)
1754      * @return currently selected disposition mode
1755      */
1756     @BackDispositionMode
getBackDisposition()1757     public int getBackDisposition() {
1758         return mBackDisposition;
1759     }
1760 
1761     /**
1762      * Return the maximum width, in pixels, available the input method.
1763      * Input methods are positioned at the bottom of the screen and, unless
1764      * running in fullscreen, will generally want to be as short as possible
1765      * so should compute their height based on their contents.  However, they
1766      * can stretch as much as needed horizontally.  The function returns to
1767      * you the maximum amount of space available horizontally, which you can
1768      * use if needed for UI placement.
1769      *
1770      * <p>In many cases this is not needed, you can just rely on the normal
1771      * view layout mechanisms to position your views within the full horizontal
1772      * space given to the input method.
1773      *
1774      * <p>Note that this value can change dynamically, in particular when the
1775      * screen orientation changes.
1776      */
getMaxWidth()1777     public int getMaxWidth() {
1778         final WindowManager windowManager = getSystemService(WindowManager.class);
1779         return WindowMetricsHelper.getBoundsExcludingNavigationBarAndCutout(
1780                 windowManager.getCurrentWindowMetrics()).width();
1781     }
1782 
1783     /**
1784      * Return the currently active InputBinding for the input method, or
1785      * null if there is none.
1786      */
getCurrentInputBinding()1787     public InputBinding getCurrentInputBinding() {
1788         return mInputBinding;
1789     }
1790 
1791     /**
1792      * Retrieve the currently active InputConnection that is bound to
1793      * the input method, or null if there is none.
1794      */
getCurrentInputConnection()1795     public InputConnection getCurrentInputConnection() {
1796         InputConnection ic = mStartedInputConnection;
1797         if (ic != null) {
1798             return ic;
1799         }
1800         return mInputConnection;
1801     }
1802 
1803     /**
1804      * Force switch to the last used input method and subtype. If the last input method didn't have
1805      * any subtypes, the framework will simply switch to the last input method with no subtype
1806      * specified.
1807      * @return true if the current input method and subtype was successfully switched to the last
1808      * used input method and subtype.
1809      */
switchToPreviousInputMethod()1810     public final boolean switchToPreviousInputMethod() {
1811         return mPrivOps.switchToPreviousInputMethod();
1812     }
1813 
1814     /**
1815      * Force switch to the next input method and subtype. If there is no IME enabled except
1816      * current IME and subtype, do nothing.
1817      * @param onlyCurrentIme if true, the framework will find the next subtype which
1818      * belongs to the current IME
1819      * @return true if the current input method and subtype was successfully switched to the next
1820      * input method and subtype.
1821      */
switchToNextInputMethod(boolean onlyCurrentIme)1822     public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
1823         return mPrivOps.switchToNextInputMethod(onlyCurrentIme);
1824     }
1825 
1826     /**
1827      * Returns true if the current IME needs to offer the users ways to switch to a next input
1828      * method (e.g. a globe key.).
1829      * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
1830      * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
1831      * <p> Note that the system determines the most appropriate next input method
1832      * and subtype in order to provide the consistent user experience in switching
1833      * between IMEs and subtypes.
1834      */
shouldOfferSwitchingToNextInputMethod()1835     public final boolean shouldOfferSwitchingToNextInputMethod() {
1836         return mPrivOps.shouldOfferSwitchingToNextInputMethod();
1837     }
1838 
getCurrentInputStarted()1839     public boolean getCurrentInputStarted() {
1840         return mInputStarted;
1841     }
1842 
getCurrentInputEditorInfo()1843     public EditorInfo getCurrentInputEditorInfo() {
1844         return mInputEditorInfo;
1845     }
1846 
reportFullscreenMode()1847     private void reportFullscreenMode() {
1848         mPrivOps.reportFullscreenModeAsync(mIsFullscreen);
1849     }
1850 
1851     /**
1852      * Re-evaluate whether the input method should be running in fullscreen
1853      * mode, and update its UI if this has changed since the last time it
1854      * was evaluated.  This will call {@link #onEvaluateFullscreenMode()} to
1855      * determine whether it should currently run in fullscreen mode.  You
1856      * can use {@link #isFullscreenMode()} to determine if the input method
1857      * is currently running in fullscreen mode.
1858      */
updateFullscreenMode()1859     public void updateFullscreenMode() {
1860         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.updateFullscreenMode");
1861         boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();
1862         boolean changed = mLastShowInputRequested != mShowInputRequested;
1863         if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
1864             changed = true;
1865             mIsFullscreen = isFullscreen;
1866             reportFullscreenMode();
1867             mFullscreenApplied = true;
1868             initialize();
1869             LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1870                     mFullscreenArea.getLayoutParams();
1871             if (isFullscreen) {
1872                 mFullscreenArea.setBackgroundDrawable(mThemeAttrs.getDrawable(
1873                         com.android.internal.R.styleable.InputMethodService_imeFullscreenBackground));
1874                 lp.height = 0;
1875                 lp.weight = 1;
1876             } else {
1877                 mFullscreenArea.setBackgroundDrawable(null);
1878                 lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
1879                 lp.weight = 0;
1880             }
1881             ((ViewGroup)mFullscreenArea.getParent()).updateViewLayout(
1882                     mFullscreenArea, lp);
1883             if (isFullscreen) {
1884                 if (mExtractView == null) {
1885                     View v = onCreateExtractTextView();
1886                     if (v != null) {
1887                         setExtractView(v);
1888                     }
1889                 }
1890                 startExtractingText(false);
1891             }
1892             updateExtractFrameVisibility();
1893         }
1894 
1895         if (changed) {
1896             onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested);
1897             mLastShowInputRequested = mShowInputRequested;
1898         }
1899         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
1900     }
1901 
1902     /**
1903      * Update the given window's parameters for the given mode.  This is called
1904      * when the window is first displayed and each time the fullscreen or
1905      * candidates only mode changes.
1906      *
1907      * <p>The default implementation makes the layout for the window
1908      * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and
1909      * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode.
1910      *
1911      * @param win The input method's window.
1912      * @param isFullscreen If true, the window is running in fullscreen mode
1913      * and intended to cover the entire application display.
1914      * @param isCandidatesOnly If true, the window is only showing the
1915      * candidates view and none of the rest of its UI.  This is mutually
1916      * exclusive with fullscreen mode.
1917      */
onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly)1918     public void onConfigureWindow(Window win, boolean isFullscreen,
1919             boolean isCandidatesOnly) {
1920         final int currentHeight = mWindow.getWindow().getAttributes().height;
1921         final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
1922         if (mIsInputViewShown && currentHeight != newHeight) {
1923             if (DEBUG) {
1924                 Log.w(TAG,"Window size has been changed. This may cause jankiness of resizing "
1925                         + "window: " + currentHeight + " -> " + newHeight);
1926             }
1927         }
1928         mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
1929     }
1930 
1931     /**
1932      * Return whether the input method is <em>currently</em> running in
1933      * fullscreen mode.  This is the mode that was last determined and
1934      * applied by {@link #updateFullscreenMode()}.
1935      */
isFullscreenMode()1936     public boolean isFullscreenMode() {
1937         return mIsFullscreen;
1938     }
1939 
1940     /**
1941      * Override this to control when the input method should run in
1942      * fullscreen mode.  The default implementation runs in fullsceen only
1943      * when the screen is in landscape mode.  If you change what
1944      * this returns, you will need to call {@link #updateFullscreenMode()}
1945      * yourself whenever the returned value may have changed to have it
1946      * re-evaluated and applied.
1947      */
onEvaluateFullscreenMode()1948     public boolean onEvaluateFullscreenMode() {
1949         Configuration config = getResources().getConfiguration();
1950         if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
1951             return false;
1952         }
1953         if (mInputEditorInfo != null
1954                 && ((mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0
1955                 // If app window has portrait orientation, regardless of what display orientation
1956                 // is, IME shouldn't use fullscreen-mode.
1957                 || (mInputEditorInfo.internalImeOptions
1958                         & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0)) {
1959             return false;
1960         }
1961         return true;
1962     }
1963 
1964     /**
1965      * Controls the visibility of the extracted text area.  This only applies
1966      * when the input method is in fullscreen mode, and thus showing extracted
1967      * text.  When false, the extracted text will not be shown, allowing some
1968      * of the application to be seen behind.  This is normally set for you
1969      * by {@link #onUpdateExtractingVisibility}.  This controls the visibility
1970      * of both the extracted text and candidate view; the latter since it is
1971      * not useful if there is no text to see.
1972      */
setExtractViewShown(boolean shown)1973     public void setExtractViewShown(boolean shown) {
1974         if (mExtractViewHidden == shown) {
1975             mExtractViewHidden = !shown;
1976             updateExtractFrameVisibility();
1977         }
1978     }
1979 
1980     /**
1981      * Return whether the fullscreen extract view is shown.  This will only
1982      * return true if {@link #isFullscreenMode()} returns true, and in that
1983      * case its value depends on the last call to
1984      * {@link #setExtractViewShown(boolean)}.  This effectively lets you
1985      * determine if the application window is entirely covered (when this
1986      * returns true) or if some part of it may be shown (if this returns
1987      * false, though if {@link #isFullscreenMode()} returns true in that case
1988      * then it is probably only a sliver of the application).
1989      */
isExtractViewShown()1990     public boolean isExtractViewShown() {
1991         return mIsFullscreen && !mExtractViewHidden;
1992     }
1993 
updateExtractFrameVisibility()1994     void updateExtractFrameVisibility() {
1995         final int vis;
1996         updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
1997 
1998         if (isFullscreenMode()) {
1999             vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
2000             // "vis" should be applied for the extract frame as well in the fullscreen mode.
2001             mExtractFrame.setVisibility(vis);
2002         } else {
2003             // mFullscreenArea visibility will according the candidate frame visibility once the
2004             // extract frame is gone.
2005             vis = mCandidatesVisibility;
2006             mExtractFrame.setVisibility(View.GONE);
2007         }
2008 
2009         if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
2010             int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
2011                     ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
2012                     : com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
2013                     0);
2014             if (animRes != 0) {
2015                 mFullscreenArea.startAnimation(AnimationUtils.loadAnimation(
2016                         this, animRes));
2017             }
2018         }
2019         mFullscreenArea.setVisibility(vis);
2020     }
2021 
2022     /**
2023      * Compute the interesting insets into your UI.  The default implementation
2024      * uses the top of the candidates frame for the visible insets, and the
2025      * top of the input frame for the content insets.  The default touchable
2026      * insets are {@link Insets#TOUCHABLE_INSETS_VISIBLE}.
2027      *
2028      * <p>Note that this method is not called when
2029      * {@link #isExtractViewShown} returns true, since
2030      * in that case the application is left as-is behind the input method and
2031      * not impacted by anything in its UI.
2032      *
2033      * @param outInsets Fill in with the current UI insets.
2034      */
onComputeInsets(Insets outInsets)2035     public void onComputeInsets(Insets outInsets) {
2036         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onComputeInsets");
2037         int[] loc = mTmpLocation;
2038         if (mInputFrame.getVisibility() == View.VISIBLE) {
2039             mInputFrame.getLocationInWindow(loc);
2040         } else {
2041             View decor = getWindow().getWindow().getDecorView();
2042             loc[1] = decor.getHeight();
2043         }
2044         if (isFullscreenMode()) {
2045             // In fullscreen mode, we never resize the underlying window.
2046             View decor = getWindow().getWindow().getDecorView();
2047             outInsets.contentTopInsets = decor.getHeight();
2048         } else {
2049             outInsets.contentTopInsets = loc[1];
2050         }
2051         if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
2052             mCandidatesFrame.getLocationInWindow(loc);
2053         }
2054         outInsets.visibleTopInsets = loc[1];
2055         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
2056         outInsets.touchableRegion.setEmpty();
2057         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
2058     }
2059 
2060     /**
2061      * Re-evaluate whether the soft input area should currently be shown, and
2062      * update its UI if this has changed since the last time it
2063      * was evaluated.  This will call {@link #onEvaluateInputViewShown()} to
2064      * determine whether the input view should currently be shown.  You
2065      * can use {@link #isInputViewShown()} to determine if the input view
2066      * is currently shown.
2067      */
updateInputViewShown()2068     public void updateInputViewShown() {
2069         boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
2070         if (mIsInputViewShown != isShown && mDecorViewVisible) {
2071             mIsInputViewShown = isShown;
2072             mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
2073             if (mInputView == null) {
2074                 initialize();
2075                 View v = onCreateInputView();
2076                 if (v != null) {
2077                     setInputView(v);
2078                 }
2079             }
2080         }
2081     }
2082 
2083     /**
2084      * Returns true if we have been asked to show our input view.
2085      */
isShowInputRequested()2086     public boolean isShowInputRequested() {
2087         return mShowInputRequested;
2088     }
2089 
2090     /**
2091      * Return whether the soft input view is <em>currently</em> shown to the
2092      * user.  This is the state that was last determined and
2093      * applied by {@link #updateInputViewShown()}.
2094      */
isInputViewShown()2095     public boolean isInputViewShown() {
2096         return mDecorViewVisible;
2097     }
2098 
2099     /**
2100      * Override this to control when the soft input area should be shown to the user.  The default
2101      * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
2102      * unless the user shows an intention to use software keyboard.  If you change what this
2103      * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
2104      * value may have changed to have it re-evaluated and applied.
2105      *
2106      * <p>When you override this method, it is recommended to call
2107      * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
2108      * returned.</p>
2109      */
2110     @CallSuper
onEvaluateInputViewShown()2111     public boolean onEvaluateInputViewShown() {
2112         if (mSettingsObserver == null) {
2113             Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
2114             return false;
2115         }
2116         if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
2117             return true;
2118         }
2119         Configuration config = getResources().getConfiguration();
2120         return config.keyboard == Configuration.KEYBOARD_NOKEYS
2121                 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
2122     }
2123 
2124     /**
2125      * Controls the visibility of the candidates display area.  By default
2126      * it is hidden.
2127      */
setCandidatesViewShown(boolean shown)2128     public void setCandidatesViewShown(boolean shown) {
2129         updateCandidatesVisibility(shown);
2130         if (!mShowInputRequested && mDecorViewVisible != shown) {
2131             // If we are being asked to show the candidates view while the app
2132             // has not asked for the input view to be shown, then we need
2133             // to update whether the window is shown.
2134             if (shown) {
2135                 showWindow(false);
2136             } else {
2137                 hideWindow();
2138             }
2139         }
2140     }
2141 
updateCandidatesVisibility(boolean shown)2142     void updateCandidatesVisibility(boolean shown) {
2143         int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
2144         if (mCandidatesVisibility != vis) {
2145             mCandidatesFrame.setVisibility(vis);
2146             mCandidatesVisibility = vis;
2147         }
2148     }
2149 
2150     /**
2151      * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
2152      * or {@link View#GONE View.GONE}) of the candidates view when it is not
2153      * shown.  The default implementation returns GONE when
2154      * {@link #isExtractViewShown} returns true,
2155      * otherwise VISIBLE.  Be careful if you change this to return GONE in
2156      * other situations -- if showing or hiding the candidates view causes
2157      * your window to resize, this can cause temporary drawing artifacts as
2158      * the resize takes place.
2159      */
getCandidatesHiddenVisibility()2160     public int getCandidatesHiddenVisibility() {
2161         return isExtractViewShown() ? View.GONE : View.INVISIBLE;
2162     }
2163 
showStatusIcon(@rawableRes int iconResId)2164     public void showStatusIcon(@DrawableRes int iconResId) {
2165         mStatusIcon = iconResId;
2166         mPrivOps.updateStatusIconAsync(getPackageName(), iconResId);
2167     }
2168 
hideStatusIcon()2169     public void hideStatusIcon() {
2170         mStatusIcon = 0;
2171         mPrivOps.updateStatusIconAsync(null, 0);
2172     }
2173 
2174     /**
2175      * Force switch to a new input method, as identified by <var>id</var>.  This
2176      * input method will be destroyed, and the requested one started on the
2177      * current input field.
2178      *
2179      * @param id Unique identifier of the new input method to start.
2180      */
switchInputMethod(String id)2181     public void switchInputMethod(String id) {
2182         mPrivOps.setInputMethod(id);
2183     }
2184 
2185     /**
2186      * Force switch to a new input method, as identified by {@code id}.  This
2187      * input method will be destroyed, and the requested one started on the
2188      * current input field.
2189      *
2190      * @param id Unique identifier of the new input method to start.
2191      * @param subtype The new subtype of the new input method to be switched to.
2192      */
switchInputMethod(String id, InputMethodSubtype subtype)2193     public final void switchInputMethod(String id, InputMethodSubtype subtype) {
2194         mPrivOps.setInputMethodAndSubtype(id, subtype);
2195     }
2196 
setExtractView(View view)2197     public void setExtractView(View view) {
2198         mExtractFrame.removeAllViews();
2199         mExtractFrame.addView(view, new FrameLayout.LayoutParams(
2200                 ViewGroup.LayoutParams.MATCH_PARENT,
2201                 ViewGroup.LayoutParams.MATCH_PARENT));
2202         mExtractView = view;
2203         if (view != null) {
2204             mExtractEditText = view.findViewById(
2205                     com.android.internal.R.id.inputExtractEditText);
2206             mExtractEditText.setIME(this);
2207             mExtractAction = view.findViewById(
2208                     com.android.internal.R.id.inputExtractAction);
2209             if (mExtractAction != null) {
2210                 mExtractAccessories = view.findViewById(
2211                         com.android.internal.R.id.inputExtractAccessories);
2212             }
2213             startExtractingText(false);
2214         } else {
2215             mExtractEditText = null;
2216             mExtractAccessories = null;
2217             mExtractAction = null;
2218         }
2219     }
2220 
2221     /**
2222      * Replaces the current candidates view with a new one.  You only need to
2223      * call this when dynamically changing the view; normally, you should
2224      * implement {@link #onCreateCandidatesView()} and create your view when
2225      * first needed by the input method.
2226      */
setCandidatesView(View view)2227     public void setCandidatesView(View view) {
2228         mCandidatesFrame.removeAllViews();
2229         mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
2230                 ViewGroup.LayoutParams.MATCH_PARENT,
2231                 ViewGroup.LayoutParams.WRAP_CONTENT));
2232     }
2233 
2234     /**
2235      * Replaces the current input view with a new one.  You only need to
2236      * call this when dynamically changing the view; normally, you should
2237      * implement {@link #onCreateInputView()} and create your view when
2238      * first needed by the input method.
2239      */
setInputView(View view)2240     public void setInputView(View view) {
2241         mInputFrame.removeAllViews();
2242         mInputFrame.addView(view, new FrameLayout.LayoutParams(
2243                 ViewGroup.LayoutParams.MATCH_PARENT,
2244                 ViewGroup.LayoutParams.WRAP_CONTENT));
2245         mInputView = view;
2246     }
2247 
2248     /**
2249      * Called by the framework to create the layout for showing extracted text.
2250      * Only called when in fullscreen mode.  The returned view hierarchy must
2251      * have an {@link ExtractEditText} whose ID is
2252      * {@link android.R.id#inputExtractEditText}, with action ID
2253      * {@link android.R.id#inputExtractAction} and accessories ID
2254      * {@link android.R.id#inputExtractAccessories}.
2255      */
onCreateExtractTextView()2256     public View onCreateExtractTextView() {
2257         return mInflater.inflate(
2258                 com.android.internal.R.layout.input_method_extract_view, null);
2259     }
2260 
2261     /**
2262      * Create and return the view hierarchy used to show candidates.  This will
2263      * be called once, when the candidates are first displayed.  You can return
2264      * null to have no candidates view; the default implementation returns null.
2265      *
2266      * <p>To control when the candidates view is displayed, use
2267      * {@link #setCandidatesViewShown(boolean)}.
2268      * To change the candidates view after the first one is created by this
2269      * function, use {@link #setCandidatesView(View)}.
2270      */
onCreateCandidatesView()2271     public View onCreateCandidatesView() {
2272         return null;
2273     }
2274 
2275     /**
2276      * Create and return the view hierarchy used for the input area (such as
2277      * a soft keyboard).  This will be called once, when the input area is
2278      * first displayed.  You can return null to have no input area; the default
2279      * implementation returns null.
2280      *
2281      * <p>To control when the input view is displayed, implement
2282      * {@link #onEvaluateInputViewShown()}.
2283      * To change the input view after the first one is created by this
2284      * function, use {@link #setInputView(View)}.
2285      */
onCreateInputView()2286     public View onCreateInputView() {
2287         return null;
2288     }
2289 
2290     /**
2291      * Called when the input view is being shown and input has started on
2292      * a new editor.  This will always be called after {@link #onStartInput},
2293      * allowing you to do your general setup there and just view-specific
2294      * setup here.  You are guaranteed that {@link #onCreateInputView()} will
2295      * have been called some time before this function is called.
2296      *
2297      * @param info Description of the type of text being edited.
2298      * @param restarting Set to true if we are restarting input on the
2299      * same text field as before.
2300      */
onStartInputView(EditorInfo info, boolean restarting)2301     public void onStartInputView(EditorInfo info, boolean restarting) {
2302         // Intentionally empty
2303     }
2304 
2305     /**
2306      * Called when the input view is being hidden from the user.  This will
2307      * be called either prior to hiding the window, or prior to switching to
2308      * another target for editing.
2309      *
2310      * <p>The default
2311      * implementation uses the InputConnection to clear any active composing
2312      * text; you can override this (not calling the base class implementation)
2313      * to perform whatever behavior you would like.
2314      *
2315      * @param finishingInput If true, {@link #onFinishInput} will be
2316      * called immediately after.
2317      */
onFinishInputView(boolean finishingInput)2318     public void onFinishInputView(boolean finishingInput) {
2319         if (!finishingInput) {
2320             InputConnection ic = getCurrentInputConnection();
2321             if (ic != null) {
2322                 ic.finishComposingText();
2323             }
2324         }
2325     }
2326 
2327     /**
2328      * Called when only the candidates view has been shown for showing
2329      * processing as the user enters text through a hard keyboard.
2330      * This will always be called after {@link #onStartInput},
2331      * allowing you to do your general setup there and just view-specific
2332      * setup here.  You are guaranteed that {@link #onCreateCandidatesView()}
2333      * will have been called some time before this function is called.
2334      *
2335      * <p>Note that this will <em>not</em> be called when the input method
2336      * is running in full editing mode, and thus receiving
2337      * {@link #onStartInputView} to initiate that operation.  This is only
2338      * for the case when candidates are being shown while the input method
2339      * editor is hidden but wants to show its candidates UI as text is
2340      * entered through some other mechanism.
2341      *
2342      * @param info Description of the type of text being edited.
2343      * @param restarting Set to true if we are restarting input on the
2344      * same text field as before.
2345      */
onStartCandidatesView(EditorInfo info, boolean restarting)2346     public void onStartCandidatesView(EditorInfo info, boolean restarting) {
2347         // Intentionally empty
2348     }
2349 
2350     /**
2351      * Called when the candidates view is being hidden from the user.  This will
2352      * be called either prior to hiding the window, or prior to switching to
2353      * another target for editing.
2354      *
2355      * <p>The default
2356      * implementation uses the InputConnection to clear any active composing
2357      * text; you can override this (not calling the base class implementation)
2358      * to perform whatever behavior you would like.
2359      *
2360      * @param finishingInput If true, {@link #onFinishInput} will be
2361      * called immediately after.
2362      */
onFinishCandidatesView(boolean finishingInput)2363     public void onFinishCandidatesView(boolean finishingInput) {
2364         if (!finishingInput) {
2365             InputConnection ic = getCurrentInputConnection();
2366             if (ic != null) {
2367                 ic.finishComposingText();
2368             }
2369         }
2370     }
2371 
2372     /**
2373      * Called to prepare stylus handwriting.
2374      * The system calls this before the {@link #onStartStylusHandwriting} request.
2375      *
2376      * <p>Note: The system tries to call this as early as possible, when it detects that
2377      * handwriting stylus input is imminent. However, that a subsequent call to
2378      * {@link #onStartStylusHandwriting} actually happens is not guaranteed.</p>
2379      */
onPrepareStylusHandwriting()2380     public void onPrepareStylusHandwriting() {
2381         // Intentionally empty
2382     }
2383 
2384     /**
2385      * Called when an app requests stylus handwriting
2386      * {@link InputMethodManager#startStylusHandwriting(View)}.
2387      *
2388      * This will always be preceded by {@link #onStartInput(EditorInfo, boolean)} for the
2389      * {@link EditorInfo} and {@link InputConnection} for which stylus handwriting is being
2390      * requested.
2391      *
2392      * If the IME supports handwriting for the current input, it should return {@code true},
2393      * ensure its inking views are attached to the {@link #getStylusHandwritingWindow()}, and handle
2394      * stylus input received from {@link #onStylusHandwritingMotionEvent(MotionEvent)} on the
2395      * {@link #getStylusHandwritingWindow()} via {@link #getCurrentInputConnection()}.
2396      * @return {@code true} if IME can honor the request, {@code false} if IME cannot at this time.
2397      */
onStartStylusHandwriting()2398     public boolean onStartStylusHandwriting() {
2399         // Intentionally empty
2400         return false;
2401     }
2402 
2403     /**
2404      * Called after {@link #onStartStylusHandwriting()} returns {@code true} for every Stylus
2405      * {@link MotionEvent}.
2406      * By default, this method forwards all {@link MotionEvent}s to the
2407      * {@link #getStylusHandwritingWindow()} once its visible, however IME can override it to
2408      * receive them sooner.
2409      * @param motionEvent {@link MotionEvent} from stylus.
2410      */
onStylusHandwritingMotionEvent(@onNull MotionEvent motionEvent)2411     public void onStylusHandwritingMotionEvent(@NonNull MotionEvent motionEvent) {
2412         if (mInkWindow.isInkViewVisible()) {
2413             mInkWindow.getDecorView().dispatchTouchEvent(motionEvent);
2414         } else {
2415             if (mPendingEvents == null) {
2416                 mPendingEvents = new RingBuffer(MotionEvent.class, MAX_EVENTS_BUFFER);
2417             }
2418             mPendingEvents.append(motionEvent);
2419             mInkWindow.setInkViewVisibilityListener(() -> {
2420                 if (mPendingEvents != null && !mPendingEvents.isEmpty()) {
2421                     for (MotionEvent event : mPendingEvents.toArray()) {
2422                         mInkWindow.getDecorView().dispatchTouchEvent(event);
2423                     }
2424                     mPendingEvents.clear();
2425                 }
2426             });
2427         }
2428     }
2429 
2430     /**
2431      * Called when the current stylus handwriting session was finished (either by the system or
2432      * via {@link #finishStylusHandwriting()}.
2433      *
2434      * When this is called, the ink window has been made invisible, and the IME no longer
2435      * intercepts handwriting-related {@code MotionEvent}s.
2436      */
onFinishStylusHandwriting()2437     public void onFinishStylusHandwriting() {
2438         // Intentionally empty
2439     }
2440 
2441     /**
2442      * Returns the stylus handwriting inking window.
2443      * IMEs supporting stylus input are expected to attach their inking views to this
2444      * window (e.g. with {@link Window#setContentView(View)} )). Handwriting-related
2445      * {@link MotionEvent}s are dispatched to the attached view hierarchy.
2446      *
2447      * Note: This returns {@code null} if IME doesn't support stylus handwriting
2448      *   i.e. if {@link InputMethodInfo#supportsStylusHandwriting()} is false.
2449      *   This method should be called after {@link #onStartStylusHandwriting()}.
2450      * @see #onStartStylusHandwriting()
2451      */
2452     @Nullable
getStylusHandwritingWindow()2453     public final Window getStylusHandwritingWindow() {
2454         return mInkWindow;
2455     }
2456 
2457     /**
2458      * Finish the current stylus handwriting session.
2459      *
2460      * This dismisses the {@link #getStylusHandwritingWindow ink window} and stops intercepting
2461      * stylus {@code MotionEvent}s.
2462      *
2463      * Note for IME developers: Call this method at any time to finish current handwriting session.
2464      * Generally, this should be invoked after a short timeout, giving the user enough time
2465      * to start the next stylus stroke, if any.
2466      *
2467      * Handwriting session will be finished by framework on next {@link #onFinishInput()}.
2468      */
finishStylusHandwriting()2469     public final void finishStylusHandwriting() {
2470         if (DEBUG) Log.v(TAG, "finishStylusHandwriting()");
2471         if (mInkWindow == null) {
2472             return;
2473         }
2474         if (!mHandwritingRequestId.isPresent()) {
2475             return;
2476         }
2477 
2478         final int requestId = mHandwritingRequestId.getAsInt();
2479         mHandwritingRequestId = OptionalInt.empty();
2480 
2481         mHandwritingEventReceiver.dispose();
2482         mHandwritingEventReceiver = null;
2483         mInkWindow.hide(false /* remove */);
2484 
2485         mPrivOps.resetStylusHandwriting(requestId);
2486         mOnPreparedStylusHwCalled = false;
2487         onFinishStylusHandwriting();
2488     }
2489 
2490     /**
2491      * The system has decided that it may be time to show your input method.
2492      * This is called due to a corresponding call to your
2493      * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
2494      * method.  The default implementation uses
2495      * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
2496      * and the current configuration to decide whether the input view should
2497      * be shown at this point.
2498      *
2499      * @param flags Provides additional information about the show request,
2500      * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
2501      * @param configChange This is true if we are re-showing due to a
2502      * configuration change.
2503      * @return Returns true to indicate that the window should be shown.
2504      */
onShowInputRequested(int flags, boolean configChange)2505     public boolean onShowInputRequested(int flags, boolean configChange) {
2506         if (!onEvaluateInputViewShown()) {
2507             return false;
2508         }
2509         if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
2510             if (!configChange && onEvaluateFullscreenMode()) {
2511                 // Don't show if this is not explicitly requested by the user and
2512                 // the input method is fullscreen.  That would be too disruptive.
2513                 // However, we skip this change for a config change, since if
2514                 // the IME is already shown we do want to go into fullscreen
2515                 // mode at this point.
2516                 return false;
2517             }
2518             if (!mSettingsObserver.shouldShowImeWithHardKeyboard() &&
2519                     getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS) {
2520                 // And if the device has a hard keyboard, even if it is
2521                 // currently hidden, don't show the input method implicitly.
2522                 // These kinds of devices don't need it that much.
2523                 return false;
2524             }
2525         }
2526         return true;
2527     }
2528 
2529     /**
2530      * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal
2531      * states depending on its result.  Since {@link #onShowInputRequested(int, boolean)} is
2532      * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
2533      * to have this method to ensure that those internal states are always updated no matter how
2534      * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
2535      * @param flags Provides additional information about the show request,
2536      * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
2537      * @param configChange This is true if we are re-showing due to a
2538      * configuration change.
2539      * @return Returns true to indicate that the window should be shown.
2540      * @see #onShowInputRequested(int, boolean)
2541      */
dispatchOnShowInputRequested(int flags, boolean configChange)2542     private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
2543         final boolean result = onShowInputRequested(flags, configChange);
2544         mInlineSuggestionSessionController.notifyOnShowInputRequested(result);
2545         if (result) {
2546             mShowInputFlags = flags;
2547         } else {
2548             mShowInputFlags = 0;
2549         }
2550         return result;
2551     }
2552 
showWindow(boolean showInput)2553     public void showWindow(boolean showInput) {
2554         if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
2555                 + " mShowInputRequested=" + mShowInputRequested
2556                 + " mViewsCreated=" + mViewsCreated
2557                 + " mDecorViewVisible=" + mDecorViewVisible
2558                 + " mWindowVisible=" + mWindowVisible
2559                 + " mInputStarted=" + mInputStarted
2560                 + " mShowInputFlags=" + mShowInputFlags);
2561 
2562         if (mInShowWindow) {
2563             Log.w(TAG, "Re-entrance in to showWindow");
2564             return;
2565         }
2566 
2567         ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
2568                 null /* icProto */);
2569         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
2570         mDecorViewWasVisible = mDecorViewVisible;
2571         mInShowWindow = true;
2572         final int previousImeWindowStatus =
2573                 (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
2574                         ? (!mWindowVisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
2575         startViews(prepareWindow(showInput));
2576         final int nextImeWindowStatus = mapToImeWindowStatus();
2577         if (previousImeWindowStatus != nextImeWindowStatus) {
2578             setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
2579         }
2580 
2581         mNavigationBarController.onWindowShown();
2582         // compute visibility
2583         onWindowShown();
2584         mWindowVisible = true;
2585 
2586         // request draw for the IME surface.
2587         if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
2588         mWindow.show();
2589         mDecorViewWasVisible = true;
2590         applyVisibilityInInsetsConsumerIfNecessary(true);
2591         cancelImeSurfaceRemoval();
2592         mInShowWindow = false;
2593         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
2594         registerCompatOnBackInvokedCallback();
2595     }
2596 
2597 
2598     /**
2599      * Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
2600      *  back dispatching is enabled. We keep the {@link KeyEvent#KEYCODE_BACK} based legacy code
2601      *  around to handle back on older devices.
2602      */
registerCompatOnBackInvokedCallback()2603     private void registerCompatOnBackInvokedCallback() {
2604         if (mBackCallbackRegistered) {
2605             return;
2606         }
2607         if (mWindow != null) {
2608             mWindow.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
2609                     OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatBackCallback);
2610             mBackCallbackRegistered = true;
2611         }
2612     }
2613 
unregisterCompatOnBackInvokedCallback()2614     private void unregisterCompatOnBackInvokedCallback() {
2615         if (!mBackCallbackRegistered) {
2616             return;
2617         }
2618         if (mWindow != null) {
2619             mWindow.getOnBackInvokedDispatcher()
2620                     .unregisterOnBackInvokedCallback(mCompatBackCallback);
2621             mBackCallbackRegistered = false;
2622         }
2623     }
2624 
createBackKeyEvent(int action, boolean isTracking)2625     private KeyEvent createBackKeyEvent(int action, boolean isTracking) {
2626         final long when = SystemClock.uptimeMillis();
2627         return new KeyEvent(when, when, action,
2628                 KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
2629                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
2630                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
2631                         | (isTracking ? KeyEvent.FLAG_TRACKING : 0),
2632                 InputDevice.SOURCE_KEYBOARD);
2633     }
2634 
prepareWindow(boolean showInput)2635     private boolean prepareWindow(boolean showInput) {
2636         boolean doShowInput = false;
2637         mDecorViewVisible = true;
2638         if (!mShowInputRequested && mInputStarted && showInput) {
2639             doShowInput = true;
2640             mShowInputRequested = true;
2641         }
2642 
2643         if (DEBUG) Log.v(TAG, "showWindow: updating UI");
2644         initialize();
2645         updateFullscreenMode();
2646         updateInputViewShown();
2647 
2648         if (!mViewsCreated) {
2649             mViewsCreated = true;
2650             initialize();
2651             if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
2652             View v = onCreateCandidatesView();
2653             if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
2654             if (v != null) {
2655                 setCandidatesView(v);
2656             }
2657         }
2658         return doShowInput;
2659     }
2660 
startViews(boolean doShowInput)2661     private void startViews(boolean doShowInput) {
2662         if (mShowInputRequested) {
2663             if (!mInputViewStarted) {
2664                 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2665                 mInputViewStarted = true;
2666                 mInlineSuggestionSessionController.notifyOnStartInputView();
2667                 onStartInputView(mInputEditorInfo, false);
2668             }
2669         } else if (!mCandidatesViewStarted) {
2670             if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2671             mCandidatesViewStarted = true;
2672             onStartCandidatesView(mInputEditorInfo, false);
2673         }
2674         if (doShowInput) startExtractingText(false);
2675     }
2676 
2677     /**
2678      * Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}.
2679      *
2680      * @param setVisible {@code true} to make it visible, false to hide it.
2681      */
applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible)2682     private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
2683         ImeTracing.getInstance().triggerServiceDump(
2684                 "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
2685                 null /* icProto */);
2686         mPrivOps.applyImeVisibilityAsync(setVisible
2687                 ? mCurShowInputToken : mCurHideInputToken, setVisible);
2688     }
2689 
finishViews(boolean finishingInput)2690     private void finishViews(boolean finishingInput) {
2691         if (mInputViewStarted) {
2692             if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
2693             mInlineSuggestionSessionController.notifyOnFinishInputView();
2694             onFinishInputView(finishingInput);
2695         } else if (mCandidatesViewStarted) {
2696             if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
2697             onFinishCandidatesView(finishingInput);
2698         }
2699         mInputViewStarted = false;
2700         mCandidatesViewStarted = false;
2701     }
2702 
hideWindow()2703     public void hideWindow() {
2704         if (DEBUG) Log.v(TAG, "CALL: hideWindow");
2705         ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
2706                 null /* icProto */);
2707         setImeWindowStatus(0, mBackDisposition);
2708         applyVisibilityInInsetsConsumerIfNecessary(false);
2709         mWindowVisible = false;
2710         finishViews(false /* finishingInput */);
2711         if (mDecorViewVisible) {
2712             // It is responsible for client and server side visibility of IME window.
2713             if (mInputView != null) {
2714                 mInputView.dispatchWindowVisibilityChanged(View.GONE);
2715             }
2716             mDecorViewVisible = false;
2717             onWindowHidden();
2718             mDecorViewWasVisible = false;
2719         }
2720         mLastWasInFullscreenMode = mIsFullscreen;
2721         updateFullscreenMode();
2722         unregisterCompatOnBackInvokedCallback();
2723     }
2724 
2725     /**
2726      * Called immediately before the input method window is shown to the user.
2727      * You could override this to prepare for the window to be shown
2728      * (update view structure etc).
2729      */
onWindowShown()2730     public void onWindowShown() {
2731         // Intentionally empty
2732     }
2733 
2734     /**
2735      * Called when the input method window has been hidden from the user,
2736      * after previously being visible.
2737      */
onWindowHidden()2738     public void onWindowHidden() {
2739         // Intentionally empty
2740     }
2741 
2742     /**
2743      * Called when a new client has bound to the input method.  This
2744      * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
2745      * and {@link #onFinishInput()} calls as the user navigates through its
2746      * UI.  Upon this call you know that {@link #getCurrentInputBinding}
2747      * and {@link #getCurrentInputConnection} return valid objects.
2748      */
onBindInput()2749     public void onBindInput() {
2750         // Intentionally empty
2751     }
2752 
2753     /**
2754      * Called when the previous bound client is no longer associated
2755      * with the input method.  After returning {@link #getCurrentInputBinding}
2756      * and {@link #getCurrentInputConnection} will no longer return
2757      * valid objects.
2758      */
onUnbindInput()2759     public void onUnbindInput() {
2760         // Intentionally empty
2761     }
2762 
2763     /**
2764      * Called to inform the input method that text input has started in an
2765      * editor.  You should use this callback to initialize the state of your
2766      * input to match the state of the editor given to it.
2767      *
2768      * @param attribute The attributes of the editor that input is starting
2769      * in.
2770      * @param restarting Set to true if input is restarting in the same
2771      * editor such as because the application has changed the text in
2772      * the editor.  Otherwise will be false, indicating this is a new
2773      * session with the editor.
2774      */
onStartInput(EditorInfo attribute, boolean restarting)2775     public void onStartInput(EditorInfo attribute, boolean restarting) {
2776         // Intentionally empty
2777     }
2778 
doFinishInput()2779     void doFinishInput() {
2780         if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
2781         ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", mDumper,
2782                 null /* icProto */);
2783         finishViews(true /* finishingInput */);
2784         if (mInputStarted) {
2785             mInlineSuggestionSessionController.notifyOnFinishInput();
2786             if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
2787             onFinishInput();
2788         }
2789         mInputStarted = false;
2790         mStartedInputConnection = null;
2791         mCurCompletions = null;
2792         if (mInkWindow != null) {
2793             finishStylusHandwriting();
2794         }
2795         // Back callback is typically unregistered in {@link #hideWindow()}, but it's possible
2796         // for {@link #doFinishInput()} to be called without {@link #hideWindow()} so we also
2797         // unregister here.
2798         // TODO(b/232341407): Add CTS to verify back behavior after screen on / off.
2799         unregisterCompatOnBackInvokedCallback();
2800     }
2801 
doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting)2802     void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
2803         if (!restarting && mInputStarted) {
2804             doFinishInput();
2805         }
2806         ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", mDumper,
2807                 null /* icProto */);
2808         mInputStarted = true;
2809         mStartedInputConnection = ic;
2810         mInputEditorInfo = attribute;
2811         initialize();
2812         mInlineSuggestionSessionController.notifyOnStartInput(
2813                 attribute == null ? null : attribute.packageName,
2814                 attribute == null ? null : attribute.autofillId);
2815         if (DEBUG) Log.v(TAG, "CALL: onStartInput");
2816         onStartInput(attribute, restarting);
2817         if (mDecorViewVisible) {
2818             if (mShowInputRequested) {
2819                 if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
2820                 mInputViewStarted = true;
2821                 mInlineSuggestionSessionController.notifyOnStartInputView();
2822                 onStartInputView(mInputEditorInfo, restarting);
2823                 startExtractingText(true);
2824             } else if (mCandidatesVisibility == View.VISIBLE) {
2825                 if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
2826                 mCandidatesViewStarted = true;
2827                 onStartCandidatesView(mInputEditorInfo, restarting);
2828             }
2829         }
2830     }
2831 
2832     /**
2833      * Called to inform the input method that text input has finished in
2834      * the last editor.  At this point there may be a call to
2835      * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
2836      * new editor, or the input method may be left idle.  This method is
2837      * <em>not</em> called when input restarts in the same editor.
2838      *
2839      * <p>The default
2840      * implementation uses the InputConnection to clear any active composing
2841      * text; you can override this (not calling the base class implementation)
2842      * to perform whatever behavior you would like.
2843      */
onFinishInput()2844     public void onFinishInput() {
2845         InputConnection ic = getCurrentInputConnection();
2846         if (ic != null) {
2847             ic.finishComposingText();
2848         }
2849     }
2850 
2851     /**
2852      * Called when the application has reported auto-completion candidates that
2853      * it would like to have the input method displayed.  Typically these are
2854      * only used when an input method is running in full-screen mode, since
2855      * otherwise the user can see and interact with the pop-up window of
2856      * completions shown by the application.
2857      *
2858      * <p>The default implementation here does nothing.
2859      */
onDisplayCompletions(CompletionInfo[] completions)2860     public void onDisplayCompletions(CompletionInfo[] completions) {
2861         // Intentionally empty
2862     }
2863 
2864     /**
2865      * Called when the application has reported new extracted text to be shown
2866      * due to changes in its current text state.  The default implementation
2867      * here places the new text in the extract edit text, when the input
2868      * method is running in fullscreen mode.
2869      */
onUpdateExtractedText(int token, ExtractedText text)2870     public void onUpdateExtractedText(int token, ExtractedText text) {
2871         if (mExtractedToken != token) {
2872             return;
2873         }
2874         if (text != null) {
2875             if (mExtractEditText != null) {
2876                 mExtractedText = text;
2877                 mExtractEditText.setExtractedText(text);
2878             }
2879         }
2880     }
2881 
2882     /**
2883      * Called when the application has reported a new selection region of
2884      * the text.  This is called whether or not the input method has requested
2885      * extracted text updates, although if so it will not receive this call
2886      * if the extracted text has changed as well.
2887      *
2888      * <p>Be careful about changing the text in reaction to this call with
2889      * methods such as setComposingText, commitText or
2890      * deleteSurroundingText. If the cursor moves as a result, this method
2891      * will be called again, which may result in an infinite loop.
2892      *
2893      * <p>The default implementation takes care of updating the cursor in
2894      * the extract text, if it is being shown.
2895      */
onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)2896     public void onUpdateSelection(int oldSelStart, int oldSelEnd,
2897             int newSelStart, int newSelEnd,
2898             int candidatesStart, int candidatesEnd) {
2899         final ExtractEditText eet = mExtractEditText;
2900         if (eet != null && isFullscreenMode() && mExtractedText != null) {
2901             final int off = mExtractedText.startOffset;
2902             eet.startInternalChanges();
2903             newSelStart -= off;
2904             newSelEnd -= off;
2905             final int len = eet.getText().length();
2906             if (newSelStart < 0) newSelStart = 0;
2907             else if (newSelStart > len) newSelStart = len;
2908             if (newSelEnd < 0) newSelEnd = 0;
2909             else if (newSelEnd > len) newSelEnd = len;
2910             eet.setSelection(newSelStart, newSelEnd);
2911             eet.finishInternalChanges();
2912         }
2913     }
2914 
2915     /**
2916      * Called when the user tapped or clicked a text view.
2917      * IMEs can't rely on this method being called because this was not part of the original IME
2918      * protocol, so applications with custom text editing written before this method appeared will
2919      * not call to inform the IME of this interaction.
2920      * @param focusChanged true if the user changed the focused view by this click.
2921      * @see InputMethodManager#viewClicked(View)
2922      * @deprecated The method may not be called for composite {@link View} that works as a giant
2923      *             "Canvas", which can host its own UI hierarchy and sub focus state.
2924      *             {@link android.webkit.WebView} is a good example. Application / IME developers
2925      *             should not rely on this method. If your goal is just being notified when an
2926      *             on-going input is interrupted, simply monitor {@link #onFinishInput()}.
2927      */
2928     @Deprecated
onViewClicked(boolean focusChanged)2929     public void onViewClicked(boolean focusChanged) {
2930         // Intentionally empty
2931     }
2932 
2933     /**
2934      * Called when the application has reported a new location of its text
2935      * cursor.  This is only called if explicitly requested by the input method.
2936      * The default implementation does nothing.
2937      * @deprecated Use {@link #onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
2938      */
2939     @Deprecated
onUpdateCursor(Rect newCursor)2940     public void onUpdateCursor(Rect newCursor) {
2941         // Intentionally empty
2942     }
2943 
2944     /**
2945      * Called when the application has reported a new location of its text insertion point and
2946      * characters in the composition string.  This is only called if explicitly requested by the
2947      * input method. The default implementation does nothing.
2948      * @param cursorAnchorInfo The positional information of the text insertion point and the
2949      * composition string.
2950      */
onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo)2951     public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
2952         // Intentionally empty
2953     }
2954 
2955     /**
2956      * Close this input method's soft input area, removing it from the display.
2957      *
2958      * The input method will continue running, but the user can no longer use it to generate input
2959      * by touching the screen.
2960      *
2961      * @see InputMethodManager#HIDE_IMPLICIT_ONLY
2962      * @see InputMethodManager#HIDE_NOT_ALWAYS
2963      * @param flags Provides additional operating flags.
2964      */
requestHideSelf(int flags)2965     public void requestHideSelf(int flags) {
2966         requestHideSelf(flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME);
2967     }
2968 
requestHideSelf(int flags, @SoftInputShowHideReason int reason)2969     private void requestHideSelf(int flags, @SoftInputShowHideReason int reason) {
2970         ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
2971                 null /* icProto */);
2972         mPrivOps.hideMySoftInput(flags, reason);
2973     }
2974 
2975     /**
2976      * Show the input method's soft input area, so the user sees the input method window and can
2977      * interact with it.
2978      *
2979      * @see InputMethodManager#SHOW_IMPLICIT
2980      * @see InputMethodManager#SHOW_FORCED
2981      * @param flags Provides additional operating flags.
2982      */
requestShowSelf(int flags)2983     public final void requestShowSelf(int flags) {
2984         ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
2985                 null /* icProto */);
2986         mPrivOps.showMySoftInput(flags);
2987     }
2988 
handleBack(boolean doIt)2989     private boolean handleBack(boolean doIt) {
2990         if (mShowInputRequested) {
2991             // If the soft input area is shown, back closes it and we
2992             // consume the back key.
2993             if (doIt) {
2994                 requestHideSelf(0 /* flags */, SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY);
2995             }
2996             return true;
2997         } else if (mDecorViewVisible) {
2998             if (mCandidatesVisibility == View.VISIBLE) {
2999                 // If we are showing candidates even if no input area, then
3000                 // hide them.
3001                 if (doIt) setCandidatesViewShown(false);
3002             } else {
3003                 // If we have the window visible for some other reason --
3004                 // most likely to show candidates -- then just get rid
3005                 // of it.  This really shouldn't happen, but just in case...
3006                 if (doIt) hideWindow();
3007             }
3008             return true;
3009         }
3010         return false;
3011     }
3012 
3013     /**
3014      * @return {@link ExtractEditText} if it is considered to be visible and active. Otherwise
3015      * {@code null} is returned.
3016      */
getExtractEditTextIfVisible()3017     private ExtractEditText getExtractEditTextIfVisible() {
3018         if (!isExtractViewShown() || !isInputViewShown()) {
3019             return null;
3020         }
3021         return mExtractEditText;
3022     }
3023 
3024     /**
3025      * Called back when a {@link KeyEvent} is forwarded from the target application.
3026      *
3027      * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK} if the IME is
3028      * currently shown , to possibly hide it when the key goes up (if not canceled or long pressed).
3029      * In addition, in fullscreen mode only, it will consume DPAD movement events to move the cursor
3030      * in the extracted text view, not allowing them to perform navigation in the underlying
3031      * application.</p>
3032      *
3033      * <p>The default implementation does not take flags specified to
3034      * {@link #setBackDisposition(int)} into account, even on API version
3035      * {@link android.os.Build.VERSION_CODES#P} and later devices.  IME developers are responsible
3036      * for making sure that their special handling for {@link KeyEvent#KEYCODE_BACK} are consistent
3037      * with the flag they specified to {@link #setBackDisposition(int)}.</p>
3038      *
3039      * @param keyCode The value in {@code event.getKeyCode()}
3040      * @param event Description of the key event
3041      *
3042      * @return {@code true} if the event is consumed by the IME and the application no longer needs
3043      *         to consume it.  Return {@code false} when the event should be handled as if the IME
3044      *         had not seen the event at all.
3045      */
onKeyDown(int keyCode, KeyEvent event)3046     public boolean onKeyDown(int keyCode, KeyEvent event) {
3047         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
3048             final ExtractEditText eet = getExtractEditTextIfVisible();
3049             if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
3050                 return true;
3051             }
3052             if (handleBack(false)) {
3053                 event.startTracking();
3054                 return true;
3055             }
3056             return false;
3057         }
3058         return doMovementKey(keyCode, event, MOVEMENT_DOWN);
3059     }
3060 
3061     /**
3062      * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
3063      * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
3064      * the event).
3065      */
onKeyLongPress(int keyCode, KeyEvent event)3066     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
3067         return false;
3068     }
3069 
3070     /**
3071      * Override this to intercept special key multiple events before they are
3072      * processed by the
3073      * application.  If you return true, the application will not itself
3074      * process the event.  If you return false, the normal application processing
3075      * will occur as if the IME had not seen the event at all.
3076      *
3077      * <p>The default implementation always returns false, except when
3078      * in fullscreen mode, where it will consume DPAD movement
3079      * events to move the cursor in the extracted text view, not allowing
3080      * them to perform navigation in the underlying application.
3081      */
onKeyMultiple(int keyCode, int count, KeyEvent event)3082     public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
3083         return doMovementKey(keyCode, event, count);
3084     }
3085 
3086     /**
3087      * Override this to intercept key up events before they are processed by the
3088      * application.  If you return true, the application will not itself
3089      * process the event.  If you return false, the normal application processing
3090      * will occur as if the IME had not seen the event at all.
3091      *
3092      * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
3093      * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown.  In
3094      * addition, in fullscreen mode only, it will consume DPAD movement
3095      * events to move the cursor in the extracted text view, not allowing
3096      * them to perform navigation in the underlying application.
3097      */
onKeyUp(int keyCode, KeyEvent event)3098     public boolean onKeyUp(int keyCode, KeyEvent event) {
3099         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
3100             final ExtractEditText eet = getExtractEditTextIfVisible();
3101             if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
3102                 return true;
3103             }
3104             if (event.isTracking() && !event.isCanceled()) {
3105                 return handleBack(true);
3106             }
3107         }
3108         return doMovementKey(keyCode, event, MOVEMENT_UP);
3109     }
3110 
3111     /**
3112      * Override this to intercept trackball motion events before they are
3113      * processed by the application.
3114      * If you return true, the application will not itself process the event.
3115      * If you return false, the normal application processing will occur as if
3116      * the IME had not seen the event at all.
3117      */
3118     @Override
onTrackballEvent(MotionEvent event)3119     public boolean onTrackballEvent(MotionEvent event) {
3120         if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
3121         return false;
3122     }
3123 
3124     /**
3125      * Override this to intercept generic motion events before they are
3126      * processed by the application.
3127      * If you return true, the application will not itself process the event.
3128      * If you return false, the normal application processing will occur as if
3129      * the IME had not seen the event at all.
3130      */
3131     @Override
onGenericMotionEvent(MotionEvent event)3132     public boolean onGenericMotionEvent(MotionEvent event) {
3133         if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
3134         return false;
3135     }
3136 
onAppPrivateCommand(String action, Bundle data)3137     public void onAppPrivateCommand(String action, Bundle data) {
3138     }
3139 
3140     /**
3141      * Handle a request by the system to toggle the soft input area.
3142      */
onToggleSoftInput(int showFlags, int hideFlags)3143     private void onToggleSoftInput(int showFlags, int hideFlags) {
3144         if (DEBUG) Log.v(TAG, "toggleSoftInput()");
3145         if (isInputViewShown()) {
3146             requestHideSelf(
3147                     hideFlags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
3148         } else {
3149             requestShowSelf(showFlags);
3150         }
3151     }
3152 
3153     static final int MOVEMENT_DOWN = -1;
3154     static final int MOVEMENT_UP = -2;
3155 
reportExtractedMovement(int keyCode, int count)3156     void reportExtractedMovement(int keyCode, int count) {
3157         int dx = 0, dy = 0;
3158         switch (keyCode) {
3159             case KeyEvent.KEYCODE_DPAD_LEFT:
3160                 dx = -count;
3161                 break;
3162             case KeyEvent.KEYCODE_DPAD_RIGHT:
3163                 dx = count;
3164                 break;
3165             case KeyEvent.KEYCODE_DPAD_UP:
3166                 dy = -count;
3167                 break;
3168             case KeyEvent.KEYCODE_DPAD_DOWN:
3169                 dy = count;
3170                 break;
3171         }
3172         onExtractedCursorMovement(dx, dy);
3173     }
3174 
doMovementKey(int keyCode, KeyEvent event, int count)3175     boolean doMovementKey(int keyCode, KeyEvent event, int count) {
3176         final ExtractEditText eet = getExtractEditTextIfVisible();
3177         if (eet != null) {
3178             // If we are in fullscreen mode, the cursor will move around
3179             // the extract edit text, but should NOT cause focus to move
3180             // to other fields.
3181             MovementMethod movement = eet.getMovementMethod();
3182             Layout layout = eet.getLayout();
3183             if (movement != null && layout != null) {
3184                 // We want our own movement method to handle the key, so the
3185                 // cursor will properly move in our own word wrapping.
3186                 if (count == MOVEMENT_DOWN) {
3187                     if (movement.onKeyDown(eet, eet.getText(), keyCode, event)) {
3188                         reportExtractedMovement(keyCode, 1);
3189                         return true;
3190                     }
3191                 } else if (count == MOVEMENT_UP) {
3192                     if (movement.onKeyUp(eet, eet.getText(), keyCode, event)) {
3193                         return true;
3194                     }
3195                 } else {
3196                     if (movement.onKeyOther(eet, eet.getText(), event)) {
3197                         reportExtractedMovement(keyCode, count);
3198                     } else {
3199                         KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
3200                         if (movement.onKeyDown(eet, eet.getText(), keyCode, down)) {
3201                             KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
3202                             movement.onKeyUp(eet, eet.getText(), keyCode, up);
3203                             while (--count > 0) {
3204                                 movement.onKeyDown(eet, eet.getText(), keyCode, down);
3205                                 movement.onKeyUp(eet, eet.getText(), keyCode, up);
3206                             }
3207                             reportExtractedMovement(keyCode, count);
3208                         }
3209                     }
3210                 }
3211             }
3212             // Regardless of whether the movement method handled the key,
3213             // we never allow DPAD navigation to the application.
3214             switch (keyCode) {
3215                 case KeyEvent.KEYCODE_DPAD_LEFT:
3216                 case KeyEvent.KEYCODE_DPAD_RIGHT:
3217                 case KeyEvent.KEYCODE_DPAD_UP:
3218                 case KeyEvent.KEYCODE_DPAD_DOWN:
3219                     return true;
3220             }
3221         }
3222 
3223         return false;
3224     }
3225 
3226     /**
3227      * Send the given key event code (as defined by {@link KeyEvent}) to the
3228      * current input connection is a key down + key up event pair.  The sent
3229      * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
3230      * set, so that the recipient can identify them as coming from a software
3231      * input method, and
3232      * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
3233      * that they don't impact the current touch mode of the UI.
3234      *
3235      * <p>Note that it's discouraged to send such key events in normal operation;
3236      * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
3237      * text fields, or for non-rich input methods. A reasonably capable software
3238      * input method should use the
3239      * {@link android.view.inputmethod.InputConnection#commitText} family of methods
3240      * to send text to an application, rather than sending key events.</p>
3241      *
3242      * @param keyEventCode The raw key code to send, as defined by
3243      * {@link KeyEvent}.
3244      */
sendDownUpKeyEvents(int keyEventCode)3245     public void sendDownUpKeyEvents(int keyEventCode) {
3246         InputConnection ic = getCurrentInputConnection();
3247         if (ic == null) return;
3248         long eventTime = SystemClock.uptimeMillis();
3249         ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
3250                 KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
3251                 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
3252         ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
3253                 KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
3254                 KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
3255     }
3256 
3257     /**
3258      * Ask the input target to execute its default action via
3259      * {@link InputConnection#performEditorAction
3260      * InputConnection.performEditorAction()}.
3261      *
3262      * <p>For compatibility, this method does not execute a custom action even if {@link
3263      * EditorInfo#actionLabel EditorInfo.actionLabel} is set. The implementor should directly call
3264      * {@link InputConnection#performEditorAction InputConnection.performEditorAction()} with
3265      * {@link EditorInfo#actionId EditorInfo.actionId} if they want to execute a custom action.</p>
3266      *
3267      * @param fromEnterKey If true, this will be executed as if the user had
3268      * pressed an enter key on the keyboard, that is it will <em>not</em>
3269      * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
3270      * EditorInfo.IME_FLAG_NO_ENTER_ACTION}.  If false, the action will be
3271      * sent regardless of how the editor has set that flag.
3272      *
3273      * @return Returns a boolean indicating whether an action has been sent.
3274      * If false, either the editor did not specify a default action or it
3275      * does not want an action from the enter key.  If true, the action was
3276      * sent (or there was no input connection at all).
3277      */
sendDefaultEditorAction(boolean fromEnterKey)3278     public boolean sendDefaultEditorAction(boolean fromEnterKey) {
3279         EditorInfo ei = getCurrentInputEditorInfo();
3280         if (ei != null &&
3281                 (!fromEnterKey || (ei.imeOptions &
3282                         EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
3283                 (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
3284                     EditorInfo.IME_ACTION_NONE) {
3285             // If the enter key was pressed, and the editor has a default
3286             // action associated with pressing enter, then send it that
3287             // explicit action instead of the key event.
3288             InputConnection ic = getCurrentInputConnection();
3289             if (ic != null) {
3290                 ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
3291             }
3292             return true;
3293         }
3294 
3295         return false;
3296     }
3297 
3298     /**
3299      * Send the given UTF-16 character to the current input connection.  Most
3300      * characters will be delivered simply by calling
3301      * {@link InputConnection#commitText InputConnection.commitText()} with
3302      * the character; some, however, may be handled different.  In particular,
3303      * the enter character ('\n') will either be delivered as an action code
3304      * or a raw key event, as appropriate.  Consider this as a convenience
3305      * method for IMEs that do not have a full implementation of actions; a
3306      * fully complying IME will decide of the right action for each event and
3307      * will likely never call this method except maybe to handle events coming
3308      * from an actual hardware keyboard.
3309      *
3310      * @param charCode The UTF-16 character code to send.
3311      */
sendKeyChar(char charCode)3312     public void sendKeyChar(char charCode) {
3313         switch (charCode) {
3314             case '\n': // Apps may be listening to an enter key to perform an action
3315                 if (!sendDefaultEditorAction(true)) {
3316                     sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
3317                 }
3318                 break;
3319             default:
3320                 // Make sure that digits go through any text watcher on the client side.
3321                 if (charCode >= '0' && charCode <= '9') {
3322                     sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
3323                 } else {
3324                     InputConnection ic = getCurrentInputConnection();
3325                     if (ic != null) {
3326                         ic.commitText(String.valueOf(charCode), 1);
3327                     }
3328                 }
3329                 break;
3330         }
3331     }
3332 
3333     /**
3334      * This is called when the user has moved the cursor in the extracted
3335      * text view, when running in fullsreen mode.  The default implementation
3336      * performs the corresponding selection change on the underlying text
3337      * editor.
3338      */
onExtractedSelectionChanged(int start, int end)3339     public void onExtractedSelectionChanged(int start, int end) {
3340         InputConnection conn = getCurrentInputConnection();
3341         if (conn != null) {
3342             conn.setSelection(start, end);
3343         }
3344     }
3345 
3346     /**
3347      * @hide
3348      */
3349     @UnsupportedAppUsage
onExtractedDeleteText(int start, int end)3350     public void onExtractedDeleteText(int start, int end) {
3351         InputConnection conn = getCurrentInputConnection();
3352         if (conn != null) {
3353             conn.finishComposingText();
3354             conn.setSelection(start, start);
3355             conn.deleteSurroundingText(0, end - start);
3356         }
3357     }
3358 
3359     /**
3360      * @hide
3361      */
3362     @UnsupportedAppUsage
onExtractedReplaceText(int start, int end, CharSequence text)3363     public void onExtractedReplaceText(int start, int end, CharSequence text) {
3364         InputConnection conn = getCurrentInputConnection();
3365         if (conn != null) {
3366             conn.setComposingRegion(start, end);
3367             conn.commitText(text, 1);
3368         }
3369     }
3370 
3371     /**
3372      * @hide
3373      */
3374     @UnsupportedAppUsage
onExtractedSetSpan(Object span, int start, int end, int flags)3375     public void onExtractedSetSpan(Object span, int start, int end, int flags) {
3376         InputConnection conn = getCurrentInputConnection();
3377         if (conn != null) {
3378             if (!conn.setSelection(start, end)) return;
3379             CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
3380             if (text instanceof Spannable) {
3381                 ((Spannable) text).setSpan(span, 0, text.length(), flags);
3382                 conn.setComposingRegion(start, end);
3383                 conn.commitText(text, 1);
3384             }
3385         }
3386     }
3387 
3388     /**
3389      * This is called when the user has clicked on the extracted text view,
3390      * when running in fullscreen mode.  The default implementation hides
3391      * the candidates view when this happens, but only if the extracted text
3392      * editor has a vertical scroll bar because its text doesn't fit.
3393      * Re-implement this to provide whatever behavior you want.
3394      */
onExtractedTextClicked()3395     public void onExtractedTextClicked() {
3396         if (mExtractEditText == null) {
3397             return;
3398         }
3399         if (mExtractEditText.hasVerticalScrollBar()) {
3400             setCandidatesViewShown(false);
3401         }
3402     }
3403 
3404     /**
3405      * This is called when the user has performed a cursor movement in the
3406      * extracted text view, when it is running in fullscreen mode.  The default
3407      * implementation hides the candidates view when a vertical movement
3408      * happens, but only if the extracted text editor has a vertical scroll bar
3409      * because its text doesn't fit.
3410      * Re-implement this to provide whatever behavior you want.
3411      * @param dx The amount of cursor movement in the x dimension.
3412      * @param dy The amount of cursor movement in the y dimension.
3413      */
onExtractedCursorMovement(int dx, int dy)3414     public void onExtractedCursorMovement(int dx, int dy) {
3415         if (mExtractEditText == null || dy == 0) {
3416             return;
3417         }
3418         if (mExtractEditText.hasVerticalScrollBar()) {
3419             setCandidatesViewShown(false);
3420         }
3421     }
3422 
3423     /**
3424      * This is called when the user has selected a context menu item from the
3425      * extracted text view, when running in fullscreen mode.  The default
3426      * implementation sends this action to the current InputConnection's
3427      * {@link InputConnection#performContextMenuAction(int)}, for it
3428      * to be processed in underlying "real" editor.  Re-implement this to
3429      * provide whatever behavior you want.
3430      */
onExtractTextContextMenuItem(int id)3431     public boolean onExtractTextContextMenuItem(int id) {
3432         InputConnection ic = getCurrentInputConnection();
3433         if (ic != null) {
3434             ic.performContextMenuAction(id);
3435         }
3436         return true;
3437     }
3438 
3439     /**
3440      * Return text that can be used as a button label for the given
3441      * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.  Returns null
3442      * if there is no action requested.  Note that there is no guarantee that
3443      * the returned text will be relatively short, so you probably do not
3444      * want to use it as text on a soft keyboard key label.
3445      *
3446      * @param imeOptions The value from {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
3447      *
3448      * @return Returns a label to use, or null if there is no action.
3449      */
getTextForImeAction(int imeOptions)3450     public CharSequence getTextForImeAction(int imeOptions) {
3451         switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
3452             case EditorInfo.IME_ACTION_NONE:
3453                 return null;
3454             case EditorInfo.IME_ACTION_GO:
3455                 return getText(com.android.internal.R.string.ime_action_go);
3456             case EditorInfo.IME_ACTION_SEARCH:
3457                 return getText(com.android.internal.R.string.ime_action_search);
3458             case EditorInfo.IME_ACTION_SEND:
3459                 return getText(com.android.internal.R.string.ime_action_send);
3460             case EditorInfo.IME_ACTION_NEXT:
3461                 return getText(com.android.internal.R.string.ime_action_next);
3462             case EditorInfo.IME_ACTION_DONE:
3463                 return getText(com.android.internal.R.string.ime_action_done);
3464             case EditorInfo.IME_ACTION_PREVIOUS:
3465                 return getText(com.android.internal.R.string.ime_action_previous);
3466             default:
3467                 return getText(com.android.internal.R.string.ime_action_default);
3468         }
3469     }
3470 
3471     /**
3472      * Return a drawable resource id that can be used as a button icon for the given
3473      * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
3474      *
3475      * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
3476      *
3477      * @return Returns a drawable resource id to use.
3478      */
3479     @DrawableRes
getIconForImeAction(int imeOptions)3480     private int getIconForImeAction(int imeOptions) {
3481         switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
3482             case EditorInfo.IME_ACTION_GO:
3483                 return com.android.internal.R.drawable.ic_input_extract_action_go;
3484             case EditorInfo.IME_ACTION_SEARCH:
3485                 return com.android.internal.R.drawable.ic_input_extract_action_search;
3486             case EditorInfo.IME_ACTION_SEND:
3487                 return com.android.internal.R.drawable.ic_input_extract_action_send;
3488             case EditorInfo.IME_ACTION_NEXT:
3489                 return com.android.internal.R.drawable.ic_input_extract_action_next;
3490             case EditorInfo.IME_ACTION_DONE:
3491                 return com.android.internal.R.drawable.ic_input_extract_action_done;
3492             case EditorInfo.IME_ACTION_PREVIOUS:
3493                 return com.android.internal.R.drawable.ic_input_extract_action_previous;
3494             default:
3495                 return com.android.internal.R.drawable.ic_input_extract_action_return;
3496         }
3497     }
3498 
3499     /**
3500      * Called when the fullscreen-mode extracting editor info has changed,
3501      * to determine whether the extracting (extract text and candidates) portion
3502      * of the UI should be shown.  The standard implementation hides or shows
3503      * the extract area depending on whether it makes sense for the
3504      * current editor.  In particular, a {@link InputType#TYPE_NULL}
3505      * input type or {@link EditorInfo#IME_FLAG_NO_EXTRACT_UI} flag will
3506      * turn off the extract area since there is no text to be shown.
3507      */
onUpdateExtractingVisibility(EditorInfo ei)3508     public void onUpdateExtractingVisibility(EditorInfo ei) {
3509         if (ei.inputType == InputType.TYPE_NULL ||
3510                 (ei.imeOptions&EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0) {
3511             // No reason to show extract UI!
3512             setExtractViewShown(false);
3513             return;
3514         }
3515 
3516         setExtractViewShown(true);
3517     }
3518 
3519     /**
3520      * Called when the fullscreen-mode extracting editor info has changed,
3521      * to update the state of its UI such as the action buttons shown.
3522      * You do not need to deal with this if you are using the standard
3523      * full screen extract UI.  If replacing it, you will need to re-implement
3524      * this to put the appropriate action button in your own UI and handle it,
3525      * and perform any other changes.
3526      *
3527      * <p>The standard implementation turns on or off its accessory area
3528      * depending on whether there is an action button, and hides or shows
3529      * the entire extract area depending on whether it makes sense for the
3530      * current editor.  In particular, a {@link InputType#TYPE_NULL} or
3531      * {@link InputType#TYPE_TEXT_VARIATION_FILTER} input type will turn off the
3532      * extract area since there is no text to be shown.
3533      */
onUpdateExtractingViews(EditorInfo ei)3534     public void onUpdateExtractingViews(EditorInfo ei) {
3535         if (!isExtractViewShown()) {
3536             return;
3537         }
3538 
3539         if (mExtractAccessories == null) {
3540             return;
3541         }
3542         final boolean hasAction = ei.actionLabel != null || (
3543                 (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
3544                 (ei.imeOptions&EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION) == 0 &&
3545                 ei.inputType != InputType.TYPE_NULL);
3546         if (hasAction) {
3547             mExtractAccessories.setVisibility(View.VISIBLE);
3548             if (mExtractAction != null) {
3549                 if (mExtractAction instanceof ImageButton) {
3550                     ((ImageButton) mExtractAction)
3551                             .setImageResource(getIconForImeAction(ei.imeOptions));
3552                     if (ei.actionLabel != null) {
3553                         mExtractAction.setContentDescription(ei.actionLabel);
3554                     } else {
3555                         mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
3556                     }
3557                 } else {
3558                     if (ei.actionLabel != null) {
3559                         ((TextView) mExtractAction).setText(ei.actionLabel);
3560                     } else {
3561                         ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
3562                     }
3563                 }
3564                 mExtractAction.setOnClickListener(mActionClickListener);
3565             }
3566         } else {
3567             mExtractAccessories.setVisibility(View.GONE);
3568             if (mExtractAction != null) {
3569                 mExtractAction.setOnClickListener(null);
3570             }
3571         }
3572     }
3573 
3574     /**
3575      * This is called when, while currently displayed in extract mode, the
3576      * current input target changes.  The default implementation will
3577      * auto-hide the IME if the new target is not a full editor, since this
3578      * can be a confusing experience for the user.
3579      */
onExtractingInputChanged(EditorInfo ei)3580     public void onExtractingInputChanged(EditorInfo ei) {
3581         if (ei.inputType == InputType.TYPE_NULL) {
3582             requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS,
3583                     SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED);
3584         }
3585     }
3586 
startExtractingText(boolean inputChanged)3587     void startExtractingText(boolean inputChanged) {
3588         final ExtractEditText eet = mExtractEditText;
3589         if (eet != null && getCurrentInputStarted()
3590                 && isFullscreenMode()) {
3591             mExtractedToken++;
3592             ExtractedTextRequest req = new ExtractedTextRequest();
3593             req.token = mExtractedToken;
3594             req.flags = InputConnection.GET_TEXT_WITH_STYLES;
3595             req.hintMaxLines = 10;
3596             req.hintMaxChars = 10000;
3597             InputConnection ic = getCurrentInputConnection();
3598             mExtractedText = ic == null? null
3599                     : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
3600             if (mExtractedText == null || ic == null) {
3601                 Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
3602                         + mExtractedText + ", input connection = " + ic);
3603             }
3604             final EditorInfo ei = getCurrentInputEditorInfo();
3605 
3606             try {
3607                 eet.startInternalChanges();
3608                 onUpdateExtractingVisibility(ei);
3609                 onUpdateExtractingViews(ei);
3610                 int inputType = ei.inputType;
3611                 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
3612                         == EditorInfo.TYPE_CLASS_TEXT) {
3613                     if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
3614                         inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
3615                     }
3616                 }
3617                 eet.setInputType(inputType);
3618                 eet.setHint(ei.hintText);
3619                 if (mExtractedText != null) {
3620                     eet.setEnabled(true);
3621                     eet.setExtractedText(mExtractedText);
3622                 } else {
3623                     eet.setEnabled(false);
3624                     eet.setText("");
3625                 }
3626             } finally {
3627                 eet.finishInternalChanges();
3628             }
3629 
3630             if (inputChanged) {
3631                 onExtractingInputChanged(ei);
3632             }
3633         }
3634     }
3635 
dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype)3636     private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3637         synchronized (mLock) {
3638             mNotifyUserActionSent = false;
3639         }
3640         onCurrentInputMethodSubtypeChanged(newSubtype);
3641     }
3642 
3643     // TODO: Handle the subtype change event
3644     /**
3645      * Called when the subtype was changed.
3646      * @param newSubtype the subtype which is being changed to.
3647      */
onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype)3648     protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
3649         if (DEBUG) {
3650             int nameResId = newSubtype.getNameResId();
3651             String mode = newSubtype.getMode();
3652             String output = "changeInputMethodSubtype:"
3653                 + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
3654                 + mode + ","
3655                 + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
3656             Log.v(TAG, "--- " + output);
3657         }
3658     }
3659 
3660     /**
3661      * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual
3662      * semantics has never been well defined.
3663      *
3664      * <p>Note that the previous document clearly mentioned that this method could return {@code 0}
3665      * at any time for whatever reason.  Now this method is just always returning {@code 0}.</p>
3666      *
3667      * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method
3668      *         always returns {@code 0}
3669      * @deprecated the actual behavior of this method has never been well defined.  You cannot use
3670      *             this method in a reliable and predictable way
3671      */
3672     @Deprecated
getInputMethodWindowRecommendedHeight()3673     public int getInputMethodWindowRecommendedHeight() {
3674         Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0."
3675                 + " Do not use this method.");
3676         return 0;
3677     }
3678 
3679     /**
3680      * Used to inject custom {@link InputMethodServiceInternal}.
3681      *
3682      * @return the {@link InputMethodServiceInternal} to be used.
3683      */
3684     @NonNull
3685     @Override
createInputMethodServiceInternal()3686     final InputMethodServiceInternal createInputMethodServiceInternal() {
3687         return new InputMethodServiceInternal() {
3688             /**
3689              * {@inheritDoc}
3690              */
3691             @NonNull
3692             @Override
3693             public Context getContext() {
3694                 return InputMethodService.this;
3695             }
3696 
3697             /**
3698              * {@inheritDoc}
3699              */
3700             @Override
3701             public void exposeContent(@NonNull InputContentInfo inputContentInfo,
3702                     @NonNull InputConnection inputConnection) {
3703                 if (inputConnection == null) {
3704                     return;
3705                 }
3706                 if (getCurrentInputConnection() != inputConnection) {
3707                     return;
3708                 }
3709                 exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
3710             }
3711 
3712             /**
3713              * {@inheritDoc}
3714              */
3715             @Override
3716             public void notifyUserActionIfNecessary() {
3717                 synchronized (mLock) {
3718                     if (mNotifyUserActionSent) {
3719                         return;
3720                     }
3721                     mPrivOps.notifyUserActionAsync();
3722                     mNotifyUserActionSent = true;
3723                 }
3724             }
3725 
3726             /**
3727              * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
3728              * permission to the content.
3729              *
3730              * <p>See {@link #exposeContent(InputContentInfo, InputConnection)} for details.</p>
3731              *
3732              * @param inputContentInfo Content to be temporarily exposed from the input method to
3733              *                         the application.  This cannot be {@code null}.
3734              * @param editorInfo The editor that receives {@link InputContentInfo}.
3735              */
3736             private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
3737                     @NonNull EditorInfo editorInfo) {
3738                 final Uri contentUri = inputContentInfo.getContentUri();
3739                 final IInputContentUriToken uriToken =
3740                         mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
3741                 if (uriToken == null) {
3742                     Log.e(TAG, "createInputContentAccessToken failed. contentUri="
3743                             + contentUri.toString() + " packageName=" + editorInfo.packageName);
3744                     return;
3745                 }
3746                 inputContentInfo.setUriToken(uriToken);
3747             }
3748 
3749             /**
3750              * {@inheritDoc}
3751              */
3752             @Override
3753             public void dump(FileDescriptor fd, PrintWriter fout, String[]args) {
3754                 InputMethodService.this.dump(fd, fout, args);
3755             }
3756 
3757             /**
3758              * {@inheritDoc}
3759              */
3760             @Override
3761             public void triggerServiceDump(String where, @Nullable byte[] icProto) {
3762                 ImeTracing.getInstance().triggerServiceDump(where, mDumper, icProto);
3763             }
3764         };
3765     }
3766 
3767     private int mapToImeWindowStatus() {
3768         return IME_ACTIVE
3769                 | (isInputViewShown() ? IME_VISIBLE : 0);
3770     }
3771 
3772     private boolean isAutomotive() {
3773         return getApplicationContext().getPackageManager().hasSystemFeature(
3774                 PackageManager.FEATURE_AUTOMOTIVE);
3775     }
3776 
3777     /**
3778      * Performs a dump of the InputMethodService's internal state.  Override
3779      * to add your own information to the dump.
3780      */
3781     @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
3782         final Printer p = new PrintWriterPrinter(fout);
3783         p.println("Input method service state for " + this + ":");
3784         p.println("  mViewsCreated=" + mViewsCreated);
3785         p.println("  mDecorViewVisible=" + mDecorViewVisible
3786                 + " mDecorViewWasVisible=" + mDecorViewWasVisible
3787                 + " mWindowVisible=" + mWindowVisible
3788                 + " mInShowWindow=" + mInShowWindow);
3789         p.println("  Configuration=" + getResources().getConfiguration());
3790         p.println("  mToken=" + mToken);
3791         p.println("  mInputBinding=" + mInputBinding);
3792         p.println("  mInputConnection=" + mInputConnection);
3793         p.println("  mStartedInputConnection=" + mStartedInputConnection);
3794         p.println("  mInputStarted=" + mInputStarted
3795                 + " mInputViewStarted=" + mInputViewStarted
3796                 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
3797 
3798         if (mInputEditorInfo != null) {
3799             p.println("  mInputEditorInfo:");
3800             mInputEditorInfo.dump(p, "    ");
3801         } else {
3802             p.println("  mInputEditorInfo: null");
3803         }
3804 
3805         p.println("  mShowInputRequested=" + mShowInputRequested
3806                 + " mLastShowInputRequested=" + mLastShowInputRequested
3807                 + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
3808         p.println("  mCandidatesVisibility=" + mCandidatesVisibility
3809                 + " mFullscreenApplied=" + mFullscreenApplied
3810                 + " mIsFullscreen=" + mIsFullscreen
3811                 + " mExtractViewHidden=" + mExtractViewHidden);
3812 
3813         if (mExtractedText != null) {
3814             p.println("  mExtractedText:");
3815             p.println("    text=" + mExtractedText.text.length() + " chars"
3816                     + " startOffset=" + mExtractedText.startOffset);
3817             p.println("    selectionStart=" + mExtractedText.selectionStart
3818                     + " selectionEnd=" + mExtractedText.selectionEnd
3819                     + " flags=0x" + Integer.toHexString(mExtractedText.flags));
3820         } else {
3821             p.println("  mExtractedText: null");
3822         }
3823         p.println("  mExtractedToken=" + mExtractedToken);
3824         p.println("  mIsInputViewShown=" + mIsInputViewShown
3825                 + " mStatusIcon=" + mStatusIcon);
3826         p.println("Last computed insets:");
3827         p.println("  contentTopInsets=" + mTmpInsets.contentTopInsets
3828                 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
3829                 + " touchableInsets=" + mTmpInsets.touchableInsets
3830                 + " touchableRegion=" + mTmpInsets.touchableRegion);
3831         p.println(" mSettingsObserver=" + mSettingsObserver);
3832         p.println(" mNavigationBarController=" + mNavigationBarController.toDebugString());
3833     }
3834 
3835     private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
3836         /**
3837          * {@inheritDoc}
3838          */
3839         @Override
3840         public void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto) {
3841             final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
3842             mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
3843             proto.write(VIEWS_CREATED, mViewsCreated);
3844             proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
3845             proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
3846             proto.write(WINDOW_VISIBLE, mWindowVisible);
3847             proto.write(IN_SHOW_WINDOW, mInShowWindow);
3848             proto.write(CONFIGURATION, getResources().getConfiguration().toString());
3849             proto.write(TOKEN, Objects.toString(mToken));
3850             proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
3851             proto.write(INPUT_STARTED, mInputStarted);
3852             proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
3853             proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
3854             if (mInputEditorInfo != null) {
3855                 mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
3856             }
3857             proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
3858             proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
3859             proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
3860             proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
3861             proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
3862             proto.write(IS_FULLSCREEN, mIsFullscreen);
3863             proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
3864             proto.write(EXTRACTED_TOKEN, mExtractedToken);
3865             proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
3866             proto.write(STATUS_ICON, mStatusIcon);
3867             mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS);
3868             proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver));
3869             if (icProto != null) {
3870                 proto.write(INPUT_CONNECTION_CALL, icProto);
3871             }
3872             proto.end(token);
3873         }
3874     };
3875 
3876     private void compatHandleBack() {
3877         if (!mDecorViewVisible) {
3878             Log.e(TAG, "Back callback invoked on a hidden IME. Removing the callback...");
3879             unregisterCompatOnBackInvokedCallback();
3880             return;
3881         }
3882         final KeyEvent downEvent = createBackKeyEvent(
3883                 KeyEvent.ACTION_DOWN, false /* isTracking */);
3884         onKeyDown(KeyEvent.KEYCODE_BACK, downEvent);
3885         final boolean hasStartedTracking =
3886                 (downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
3887         final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
3888         onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
3889     }
3890 }
3891