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