• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package android.view.inputmethod;
18 
19 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemService;
25 import android.content.Context;
26 import android.graphics.Rect;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.os.ResultReceiver;
35 import android.os.ServiceManager;
36 import android.os.ServiceManager.ServiceNotFoundException;
37 import android.os.Trace;
38 import android.text.style.SuggestionSpan;
39 import android.util.Log;
40 import android.util.Pools.Pool;
41 import android.util.Pools.SimplePool;
42 import android.util.PrintWriterPrinter;
43 import android.util.Printer;
44 import android.util.SparseArray;
45 import android.view.InputChannel;
46 import android.view.InputEvent;
47 import android.view.InputEventSender;
48 import android.view.KeyEvent;
49 import android.view.View;
50 import android.view.ViewRootImpl;
51 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
52 
53 import com.android.internal.inputmethod.IInputContentUriToken;
54 import com.android.internal.os.SomeArgs;
55 import com.android.internal.view.IInputConnectionWrapper;
56 import com.android.internal.view.IInputContext;
57 import com.android.internal.view.IInputMethodClient;
58 import com.android.internal.view.IInputMethodManager;
59 import com.android.internal.view.IInputMethodSession;
60 import com.android.internal.view.InputBindResult;
61 import com.android.internal.view.InputMethodClient;
62 
63 import java.io.FileDescriptor;
64 import java.io.PrintWriter;
65 import java.util.ArrayList;
66 import java.util.Arrays;
67 import java.util.HashMap;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Objects;
71 import java.util.concurrent.CountDownLatch;
72 import java.util.concurrent.TimeUnit;
73 
74 /**
75  * Central system API to the overall input method framework (IMF) architecture,
76  * which arbitrates interaction between applications and the current input method.
77  *
78  * <p>Topics covered here:
79  * <ol>
80  * <li><a href="#ArchitectureOverview">Architecture Overview</a>
81  * <li><a href="#Applications">Applications</a>
82  * <li><a href="#InputMethods">Input Methods</a>
83  * <li><a href="#Security">Security</a>
84  * </ol>
85  *
86  * <a name="ArchitectureOverview"></a>
87  * <h3>Architecture Overview</h3>
88  *
89  * <p>There are three primary parties involved in the input method
90  * framework (IMF) architecture:</p>
91  *
92  * <ul>
93  * <li> The <strong>input method manager</strong> as expressed by this class
94  * is the central point of the system that manages interaction between all
95  * other parts.  It is expressed as the client-side API here which exists
96  * in each application context and communicates with a global system service
97  * that manages the interaction across all processes.
98  * <li> An <strong>input method (IME)</strong> implements a particular
99  * interaction model allowing the user to generate text.  The system binds
100  * to the current input method that is in use, causing it to be created and run,
101  * and tells it when to hide and show its UI.  Only one IME is running at a time.
102  * <li> Multiple <strong>client applications</strong> arbitrate with the input
103  * method manager for input focus and control over the state of the IME.  Only
104  * one such client is ever active (working with the IME) at a time.
105  * </ul>
106  *
107  *
108  * <a name="Applications"></a>
109  * <h3>Applications</h3>
110  *
111  * <p>In most cases, applications that are using the standard
112  * {@link android.widget.TextView} or its subclasses will have little they need
113  * to do to work well with soft input methods.  The main things you need to
114  * be aware of are:</p>
115  *
116  * <ul>
117  * <li> Properly set the {@link android.R.attr#inputType} in your editable
118  * text views, so that the input method will have enough context to help the
119  * user in entering text into them.
120  * <li> Deal well with losing screen space when the input method is
121  * displayed.  Ideally an application should handle its window being resized
122  * smaller, but it can rely on the system performing panning of the window
123  * if needed.  You should set the {@link android.R.attr#windowSoftInputMode}
124  * attribute on your activity or the corresponding values on windows you
125  * create to help the system determine whether to pan or resize (it will
126  * try to determine this automatically but may get it wrong).
127  * <li> You can also control the preferred soft input state (open, closed, etc)
128  * for your window using the same {@link android.R.attr#windowSoftInputMode}
129  * attribute.
130  * </ul>
131  *
132  * <p>More finer-grained control is available through the APIs here to directly
133  * interact with the IMF and its IME -- either showing or hiding the input
134  * area, letting the user pick an input method, etc.</p>
135  *
136  * <p>For the rare people amongst us writing their own text editors, you
137  * will need to implement {@link android.view.View#onCreateInputConnection}
138  * to return a new instance of your own {@link InputConnection} interface
139  * allowing the IME to interact with your editor.</p>
140  *
141  *
142  * <a name="InputMethods"></a>
143  * <h3>Input Methods</h3>
144  *
145  * <p>An input method (IME) is implemented
146  * as a {@link android.app.Service}, typically deriving from
147  * {@link android.inputmethodservice.InputMethodService}.  It must provide
148  * the core {@link InputMethod} interface, though this is normally handled by
149  * {@link android.inputmethodservice.InputMethodService} and implementors will
150  * only need to deal with the higher-level API there.</p>
151  *
152  * See the {@link android.inputmethodservice.InputMethodService} class for
153  * more information on implementing IMEs.
154  *
155  *
156  * <a name="Security"></a>
157  * <h3>Security</h3>
158  *
159  * <p>There are a lot of security issues associated with input methods,
160  * since they essentially have freedom to completely drive the UI and monitor
161  * everything the user enters.  The Android input method framework also allows
162  * arbitrary third party IMEs, so care must be taken to restrict their
163  * selection and interactions.</p>
164  *
165  * <p>Here are some key points about the security architecture behind the
166  * IMF:</p>
167  *
168  * <ul>
169  * <li> <p>Only the system is allowed to directly access an IME's
170  * {@link InputMethod} interface, via the
171  * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission.  This is
172  * enforced in the system by not binding to an input method service that does
173  * not require this permission, so the system can guarantee no other untrusted
174  * clients are accessing the current input method outside of its control.</p>
175  *
176  * <li> <p>There may be many client processes of the IMF, but only one may
177  * be active at a time.  The inactive clients can not interact with key
178  * parts of the IMF through the mechanisms described below.</p>
179  *
180  * <li> <p>Clients of an input method are only given access to its
181  * {@link InputMethodSession} interface.  One instance of this interface is
182  * created for each client, and only calls from the session associated with
183  * the active client will be processed by the current IME.  This is enforced
184  * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
185  * IMEs, but must be explicitly handled by an IME that is customizing the
186  * raw {@link InputMethodSession} implementation.</p>
187  *
188  * <li> <p>Only the active client's {@link InputConnection} will accept
189  * operations.  The IMF tells each client process whether it is active, and
190  * the framework enforces that in inactive processes calls on to the current
191  * InputConnection will be ignored.  This ensures that the current IME can
192  * only deliver events and text edits to the UI that the user sees as
193  * being in focus.</p>
194  *
195  * <li> <p>An IME can never interact with an {@link InputConnection} while
196  * the screen is off.  This is enforced by making all clients inactive while
197  * the screen is off, and prevents bad IMEs from driving the UI when the user
198  * can not be aware of its behavior.</p>
199  *
200  * <li> <p>A client application can ask that the system let the user pick a
201  * new IME, but can not programmatically switch to one itself.  This avoids
202  * malicious applications from switching the user to their own IME, which
203  * remains running when the user navigates away to another application.  An
204  * IME, on the other hand, <em>is</em> allowed to programmatically switch
205  * the system to another IME, since it already has full control of user
206  * input.</p>
207  *
208  * <li> <p>The user must explicitly enable a new IME in settings before
209  * they can switch to it, to confirm with the system that they know about it
210  * and want to make it available for use.</p>
211  * </ul>
212  */
213 @SystemService(Context.INPUT_METHOD_SERVICE)
214 public final class InputMethodManager {
215     static final boolean DEBUG = false;
216     static final String TAG = "InputMethodManager";
217 
218     static final String PENDING_EVENT_COUNTER = "aq:imm";
219 
220     static InputMethodManager sInstance;
221 
222     /**
223      * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
224      * the window has input focus.
225      */
226     public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0;
227 
228     /**
229      * @hide Flag for IInputMethodManager.windowGainedFocus: the focus
230      * is a text editor.
231      */
232     public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1;
233 
234     /**
235      * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first
236      * time the window has gotten focus.
237      */
238     public static final int CONTROL_WINDOW_FIRST = 1<<2;
239 
240     /**
241      * @hide Flag for IInputMethodManager.startInput: this is the first
242      * time the window has gotten focus.
243      */
244     public static final int CONTROL_START_INITIAL = 1<<8;
245 
246     /**
247      * Timeout in milliseconds for delivering a key to an IME.
248      */
249     static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
250 
251     /** @hide */
252     public static final int DISPATCH_IN_PROGRESS = -1;
253 
254     /** @hide */
255     public static final int DISPATCH_NOT_HANDLED = 0;
256 
257     /** @hide */
258     public static final int DISPATCH_HANDLED = 1;
259 
260     /** @hide */
261     public static final int SHOW_IM_PICKER_MODE_AUTO = 0;
262     /** @hide */
263     public static final int SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES = 1;
264     /** @hide */
265     public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2;
266 
267     final IInputMethodManager mService;
268     final Looper mMainLooper;
269 
270     // For scheduling work on the main thread.  This also serves as our
271     // global lock.
272     final H mH;
273 
274     // Our generic input connection if the current target does not have its own.
275     final IInputContext mIInputContext;
276 
277     /**
278      * True if this input method client is active, initially false.
279      */
280     boolean mActive = false;
281 
282     /**
283      * Set whenever this client becomes inactive, to know we need to reset
284      * state with the IME the next time we receive focus.
285      */
286     boolean mHasBeenInactive = true;
287 
288     /**
289      * As reported by IME through InputConnection.
290      */
291     boolean mFullscreenMode;
292 
293     // -----------------------------------------------------------
294 
295     /**
296      * This is the root view of the overall window that currently has input
297      * method focus.
298      */
299     View mCurRootView;
300     /**
301      * This is the view that should currently be served by an input method,
302      * regardless of the state of setting that up.
303      */
304     View mServedView;
305     /**
306      * This is then next view that will be served by the input method, when
307      * we get around to updating things.
308      */
309     View mNextServedView;
310     /**
311      * This is set when we are in the process of connecting, to determine
312      * when we have actually finished.
313      */
314     boolean mServedConnecting;
315     /**
316      * This is non-null when we have connected the served view; it holds
317      * the attributes that were last retrieved from the served view and given
318      * to the input connection.
319      */
320     EditorInfo mCurrentTextBoxAttribute;
321     /**
322      * The InputConnection that was last retrieved from the served view.
323      */
324     ControlledInputConnectionWrapper mServedInputConnectionWrapper;
325     /**
326      * The completions that were last provided by the served view.
327      */
328     CompletionInfo[] mCompletions;
329 
330     // Cursor position on the screen.
331     Rect mTmpCursorRect = new Rect();
332     Rect mCursorRect = new Rect();
333     int mCursorSelStart;
334     int mCursorSelEnd;
335     int mCursorCandStart;
336     int mCursorCandEnd;
337 
338     /**
339      * Represents an invalid action notification sequence number. {@link InputMethodManagerService}
340      * always issues a positive integer for action notification sequence numbers. Thus -1 is
341      * guaranteed to be different from any valid sequence number.
342      */
343     private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1;
344     /**
345      * The next sequence number that is to be sent to {@link InputMethodManagerService} via
346      * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed.
347      */
348     private int mNextUserActionNotificationSequenceNumber =
349             NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
350 
351     /**
352      * The last sequence number that is already sent to {@link InputMethodManagerService}.
353      */
354     private int mLastSentUserActionNotificationSequenceNumber =
355             NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
356 
357     /**
358      * The instance that has previously been sent to the input method.
359      */
360     private CursorAnchorInfo mCursorAnchorInfo = null;
361 
362     // -----------------------------------------------------------
363 
364     /**
365      * Sequence number of this binding, as returned by the server.
366      */
367     int mBindSequence = -1;
368     /**
369      * ID of the method we are bound to.
370      */
371     String mCurId;
372     /**
373      * The actual instance of the method to make calls on it.
374      */
375     IInputMethodSession mCurMethod;
376     InputChannel mCurChannel;
377     ImeInputEventSender mCurSender;
378 
379     private static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE = 0x0;
380 
381     /**
382      * The monitor mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}.
383      */
384     private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
385 
386     final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
387     final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
388 
389     // -----------------------------------------------------------
390 
391     static final int MSG_DUMP = 1;
392     static final int MSG_BIND = 2;
393     static final int MSG_UNBIND = 3;
394     static final int MSG_SET_ACTIVE = 4;
395     static final int MSG_SEND_INPUT_EVENT = 5;
396     static final int MSG_TIMEOUT_INPUT_EVENT = 6;
397     static final int MSG_FLUSH_INPUT_EVENT = 7;
398     static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9;
399     static final int MSG_REPORT_FULLSCREEN_MODE = 10;
400 
401     class H extends Handler {
H(Looper looper)402         H(Looper looper) {
403             super(looper, null, true);
404         }
405 
406         @Override
handleMessage(Message msg)407         public void handleMessage(Message msg) {
408             switch (msg.what) {
409                 case MSG_DUMP: {
410                     SomeArgs args = (SomeArgs)msg.obj;
411                     try {
412                         doDump((FileDescriptor)args.arg1,
413                                 (PrintWriter)args.arg2, (String[])args.arg3);
414                     } catch (RuntimeException e) {
415                         ((PrintWriter)args.arg2).println("Exception: " + e);
416                     }
417                     synchronized (args.arg4) {
418                         ((CountDownLatch)args.arg4).countDown();
419                     }
420                     args.recycle();
421                     return;
422                 }
423                 case MSG_BIND: {
424                     final InputBindResult res = (InputBindResult)msg.obj;
425                     if (DEBUG) {
426                         Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
427                     }
428                     synchronized (mH) {
429                         if (mBindSequence < 0 || mBindSequence != res.sequence) {
430                             Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
431                                     + ", given seq=" + res.sequence);
432                             if (res.channel != null && res.channel != mCurChannel) {
433                                 res.channel.dispose();
434                             }
435                             return;
436                         }
437 
438                         mRequestUpdateCursorAnchorInfoMonitorMode =
439                                 REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
440 
441                         setInputChannelLocked(res.channel);
442                         mCurMethod = res.method;
443                         mCurId = res.id;
444                         mBindSequence = res.sequence;
445                     }
446                     startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
447                             null, 0, 0, 0);
448                     return;
449                 }
450                 case MSG_UNBIND: {
451                     final int sequence = msg.arg1;
452                     @InputMethodClient.UnbindReason
453                     final int reason = msg.arg2;
454                     if (DEBUG) {
455                         Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence +
456                                 " reason=" + InputMethodClient.getUnbindReason(reason));
457                     }
458                     final boolean startInput;
459                     synchronized (mH) {
460                         if (mBindSequence != sequence) {
461                             return;
462                         }
463                         clearBindingLocked();
464                         // If we were actively using the last input method, then
465                         // we would like to re-connect to the next input method.
466                         if (mServedView != null && mServedView.isFocused()) {
467                             mServedConnecting = true;
468                         }
469                         startInput = mActive;
470                     }
471                     if (startInput) {
472                         startInputInner(
473                                 InputMethodClient.START_INPUT_REASON_UNBOUND_FROM_IMMS, null, 0, 0,
474                                 0);
475                     }
476                     return;
477                 }
478                 case MSG_SET_ACTIVE: {
479                     final boolean active = msg.arg1 != 0;
480                     final boolean fullscreen = msg.arg2 != 0;
481                     if (DEBUG) {
482                         Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive);
483                     }
484                     synchronized (mH) {
485                         mActive = active;
486                         mFullscreenMode = fullscreen;
487                         if (!active) {
488                             // Some other client has starting using the IME, so note
489                             // that this happened and make sure our own editor's
490                             // state is reset.
491                             mHasBeenInactive = true;
492                             try {
493                                 // Note that finishComposingText() is allowed to run
494                                 // even when we are not active.
495                                 mIInputContext.finishComposingText();
496                             } catch (RemoteException e) {
497                             }
498                         }
499                         // Check focus again in case that "onWindowFocus" is called before
500                         // handling this message.
501                         if (mServedView != null && mServedView.hasWindowFocus()) {
502                             if (checkFocusNoStartInput(mHasBeenInactive)) {
503                                 final int reason = active ?
504                                         InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
505                                         InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
506                                 startInputInner(reason, null, 0, 0, 0);
507                             }
508                         }
509                     }
510                     return;
511                 }
512                 case MSG_SEND_INPUT_EVENT: {
513                     sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
514                     return;
515                 }
516                 case MSG_TIMEOUT_INPUT_EVENT: {
517                     finishedInputEvent(msg.arg1, false, true);
518                     return;
519                 }
520                 case MSG_FLUSH_INPUT_EVENT: {
521                     finishedInputEvent(msg.arg1, false, false);
522                     return;
523                 }
524                 case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
525                     synchronized (mH) {
526                         mNextUserActionNotificationSequenceNumber = msg.arg1;
527                     }
528                     return;
529                 }
530                 case MSG_REPORT_FULLSCREEN_MODE: {
531                     final boolean fullscreen = msg.arg1 != 0;
532                     InputConnection ic = null;
533                     synchronized (mH) {
534                         mFullscreenMode = fullscreen;
535                         if (mServedInputConnectionWrapper != null) {
536                             ic = mServedInputConnectionWrapper.getInputConnection();
537                         }
538                     }
539                     if (ic != null) {
540                         ic.reportFullscreenMode(fullscreen);
541                     }
542                     return;
543                 }
544             }
545         }
546     }
547 
548     private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
549         private final InputMethodManager mParentInputMethodManager;
550 
ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn, final InputMethodManager inputMethodManager)551         public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
552                 final InputMethodManager inputMethodManager) {
553             super(mainLooper, conn);
554             mParentInputMethodManager = inputMethodManager;
555         }
556 
557         @Override
isActive()558         public boolean isActive() {
559             return mParentInputMethodManager.mActive && !isFinished();
560         }
561 
deactivate()562         void deactivate() {
563             if (isFinished()) {
564                 // This is a small performance optimization.  Still only the 1st call of
565                 // reportFinish() will take effect.
566                 return;
567             }
568             closeConnection();
569         }
570 
571         @Override
onUserAction()572         protected void onUserAction() {
573             mParentInputMethodManager.notifyUserAction();
574         }
575 
576         @Override
toString()577         public String toString() {
578             return "ControlledInputConnectionWrapper{"
579                     + "connection=" + getInputConnection()
580                     + " finished=" + isFinished()
581                     + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
582                     + "}";
583         }
584     }
585 
586     final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
587         @Override
588         protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
589             // No need to check for dump permission, since we only give this
590             // interface to the system.
591             CountDownLatch latch = new CountDownLatch(1);
592             SomeArgs sargs = SomeArgs.obtain();
593             sargs.arg1 = fd;
594             sargs.arg2 = fout;
595             sargs.arg3 = args;
596             sargs.arg4 = latch;
597             mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
598             try {
599                 if (!latch.await(5, TimeUnit.SECONDS)) {
600                     fout.println("Timeout waiting for dump");
601                 }
602             } catch (InterruptedException e) {
603                 fout.println("Interrupted waiting for dump");
604             }
605         }
606 
607         @Override
608         public void setUsingInputMethod(boolean state) {
609         }
610 
611         @Override
612         public void onBindMethod(InputBindResult res) {
613             mH.obtainMessage(MSG_BIND, res).sendToTarget();
614         }
615 
616         @Override
617         public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
618             mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
619         }
620 
621         @Override
622         public void setActive(boolean active, boolean fullscreen) {
623             mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0).sendToTarget();
624         }
625 
626         @Override
627         public void setUserActionNotificationSequenceNumber(int sequenceNumber) {
628             mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, sequenceNumber, 0)
629                     .sendToTarget();
630         }
631 
632         @Override
633         public void reportFullscreenMode(boolean fullscreen) {
634             mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0)
635                     .sendToTarget();
636         }
637 
638     };
639 
640     final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
641 
InputMethodManager(Looper looper)642     InputMethodManager(Looper looper) throws ServiceNotFoundException {
643         this(IInputMethodManager.Stub.asInterface(
644                 ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
645     }
646 
InputMethodManager(IInputMethodManager service, Looper looper)647     InputMethodManager(IInputMethodManager service, Looper looper) {
648         mService = service;
649         mMainLooper = looper;
650         mH = new H(looper);
651         mIInputContext = new ControlledInputConnectionWrapper(looper,
652                 mDummyInputConnection, this);
653     }
654 
655     /**
656      * Retrieve the global InputMethodManager instance, creating it if it
657      * doesn't already exist.
658      * @hide
659      */
getInstance()660     public static InputMethodManager getInstance() {
661         synchronized (InputMethodManager.class) {
662             if (sInstance == null) {
663                 try {
664                     sInstance = new InputMethodManager(Looper.getMainLooper());
665                 } catch (ServiceNotFoundException e) {
666                     throw new IllegalStateException(e);
667                 }
668             }
669             return sInstance;
670         }
671     }
672 
673     /**
674      * Private optimization: retrieve the global InputMethodManager instance,
675      * if it exists.
676      * @hide
677      */
peekInstance()678     public static InputMethodManager peekInstance() {
679         return sInstance;
680     }
681 
682     /** @hide */
getClient()683     public IInputMethodClient getClient() {
684         return mClient;
685     }
686 
687     /** @hide */
getInputContext()688     public IInputContext getInputContext() {
689         return mIInputContext;
690     }
691 
getInputMethodList()692     public List<InputMethodInfo> getInputMethodList() {
693         try {
694             return mService.getInputMethodList();
695         } catch (RemoteException e) {
696             throw e.rethrowFromSystemServer();
697         }
698     }
699 
getEnabledInputMethodList()700     public List<InputMethodInfo> getEnabledInputMethodList() {
701         try {
702             return mService.getEnabledInputMethodList();
703         } catch (RemoteException e) {
704             throw e.rethrowFromSystemServer();
705         }
706     }
707 
708     /**
709      * Returns a list of enabled input method subtypes for the specified input method info.
710      * @param imi An input method info whose subtypes list will be returned.
711      * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
712      * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
713      * will implicitly enable subtypes according to the current system language.
714      */
getEnabledInputMethodSubtypeList(InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes)715     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
716             boolean allowsImplicitlySelectedSubtypes) {
717         try {
718             return mService.getEnabledInputMethodSubtypeList(
719                     imi == null ? null : imi.getId(), allowsImplicitlySelectedSubtypes);
720         } catch (RemoteException e) {
721             throw e.rethrowFromSystemServer();
722         }
723     }
724 
showStatusIcon(IBinder imeToken, String packageName, int iconId)725     public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
726         try {
727             mService.updateStatusIcon(imeToken, packageName, iconId);
728         } catch (RemoteException e) {
729             throw e.rethrowFromSystemServer();
730         }
731     }
732 
hideStatusIcon(IBinder imeToken)733     public void hideStatusIcon(IBinder imeToken) {
734         try {
735             mService.updateStatusIcon(imeToken, null, 0);
736         } catch (RemoteException e) {
737             throw e.rethrowFromSystemServer();
738         }
739     }
740 
741     /** @hide */
setImeWindowStatus(IBinder imeToken, IBinder startInputToken, int vis, int backDisposition)742     public void setImeWindowStatus(IBinder imeToken, IBinder startInputToken, int vis,
743             int backDisposition) {
744         try {
745             mService.setImeWindowStatus(imeToken, startInputToken, vis, backDisposition);
746         } catch (RemoteException e) {
747             throw e.rethrowFromSystemServer();
748         }
749     }
750 
751     /** @hide */
registerSuggestionSpansForNotification(SuggestionSpan[] spans)752     public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
753         try {
754             mService.registerSuggestionSpansForNotification(spans);
755         } catch (RemoteException e) {
756             throw e.rethrowFromSystemServer();
757         }
758     }
759 
760     /** @hide */
notifySuggestionPicked(SuggestionSpan span, String originalString, int index)761     public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
762         try {
763             mService.notifySuggestionPicked(span, originalString, index);
764         } catch (RemoteException e) {
765             throw e.rethrowFromSystemServer();
766         }
767     }
768 
769     /**
770      * Allows you to discover whether the attached input method is running
771      * in fullscreen mode.  Return true if it is fullscreen, entirely covering
772      * your UI, else returns false.
773      */
isFullscreenMode()774     public boolean isFullscreenMode() {
775         synchronized (mH) {
776             return mFullscreenMode;
777         }
778     }
779 
780     /**
781      * @hide
782      */
reportFullscreenMode(IBinder token, boolean fullscreen)783     public void reportFullscreenMode(IBinder token, boolean fullscreen) {
784         try {
785             mService.reportFullscreenMode(token, fullscreen);
786         } catch (RemoteException e) {
787             throw e.rethrowFromSystemServer();
788         }
789     }
790 
791     /**
792      * Return true if the given view is the currently active view for the
793      * input method.
794      */
isActive(View view)795     public boolean isActive(View view) {
796         checkFocus();
797         synchronized (mH) {
798             return (mServedView == view
799                     || (mServedView != null
800                             && mServedView.checkInputConnectionProxy(view)))
801                     && mCurrentTextBoxAttribute != null;
802         }
803     }
804 
805     /**
806      * Return true if any view is currently active in the input method.
807      */
isActive()808     public boolean isActive() {
809         checkFocus();
810         synchronized (mH) {
811             return mServedView != null && mCurrentTextBoxAttribute != null;
812         }
813     }
814 
815     /**
816      * Return true if the currently served view is accepting full text edits.
817      * If false, it has no input connection, so can only handle raw key events.
818      */
isAcceptingText()819     public boolean isAcceptingText() {
820         checkFocus();
821         return mServedInputConnectionWrapper != null &&
822                 mServedInputConnectionWrapper.getInputConnection() != null;
823     }
824 
825     /**
826      * Reset all of the state associated with being bound to an input method.
827      */
clearBindingLocked()828     void clearBindingLocked() {
829         if (DEBUG) Log.v(TAG, "Clearing binding!");
830         clearConnectionLocked();
831         setInputChannelLocked(null);
832         mBindSequence = -1;
833         mCurId = null;
834         mCurMethod = null;
835     }
836 
setInputChannelLocked(InputChannel channel)837     void setInputChannelLocked(InputChannel channel) {
838         if (mCurChannel != channel) {
839             if (mCurSender != null) {
840                 flushPendingEventsLocked();
841                 mCurSender.dispose();
842                 mCurSender = null;
843             }
844             if (mCurChannel != null) {
845                 mCurChannel.dispose();
846             }
847             mCurChannel = channel;
848         }
849     }
850 
851     /**
852      * Reset all of the state associated with a served view being connected
853      * to an input method
854      */
clearConnectionLocked()855     void clearConnectionLocked() {
856         mCurrentTextBoxAttribute = null;
857         if (mServedInputConnectionWrapper != null) {
858             mServedInputConnectionWrapper.deactivate();
859             mServedInputConnectionWrapper = null;
860         }
861     }
862 
863     /**
864      * Disconnect any existing input connection, clearing the served view.
865      */
finishInputLocked()866     void finishInputLocked() {
867         mNextServedView = null;
868         if (mServedView != null) {
869             if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
870             if (mCurrentTextBoxAttribute != null) {
871                 try {
872                     mService.finishInput(mClient);
873                 } catch (RemoteException e) {
874                     throw e.rethrowFromSystemServer();
875                 }
876             }
877             mServedView = null;
878             mCompletions = null;
879             mServedConnecting = false;
880             clearConnectionLocked();
881         }
882     }
883 
displayCompletions(View view, CompletionInfo[] completions)884     public void displayCompletions(View view, CompletionInfo[] completions) {
885         checkFocus();
886         synchronized (mH) {
887             if (mServedView != view && (mServedView == null
888                             || !mServedView.checkInputConnectionProxy(view))) {
889                 return;
890             }
891 
892             mCompletions = completions;
893             if (mCurMethod != null) {
894                 try {
895                     mCurMethod.displayCompletions(mCompletions);
896                 } catch (RemoteException e) {
897                 }
898             }
899         }
900     }
901 
updateExtractedText(View view, int token, ExtractedText text)902     public void updateExtractedText(View view, int token, ExtractedText text) {
903         checkFocus();
904         synchronized (mH) {
905             if (mServedView != view && (mServedView == null
906                     || !mServedView.checkInputConnectionProxy(view))) {
907                 return;
908             }
909 
910             if (mCurMethod != null) {
911                 try {
912                     mCurMethod.updateExtractedText(token, text);
913                 } catch (RemoteException e) {
914                 }
915             }
916         }
917     }
918 
919     /**
920      * Flag for {@link #showSoftInput} to indicate that this is an implicit
921      * request to show the input window, not as the result of a direct request
922      * by the user.  The window may not be shown in this case.
923      */
924     public static final int SHOW_IMPLICIT = 0x0001;
925 
926     /**
927      * Flag for {@link #showSoftInput} to indicate that the user has forced
928      * the input method open (such as by long-pressing menu) so it should
929      * not be closed until they explicitly do so.
930      */
931     public static final int SHOW_FORCED = 0x0002;
932 
933     /**
934      * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
935      * a result receiver: explicitly request that the current input method's
936      * soft input area be shown to the user, if needed.
937      *
938      * @param view The currently focused view, which would like to receive
939      * soft keyboard input.
940      * @param flags Provides additional operating flags.  Currently may be
941      * 0 or have the {@link #SHOW_IMPLICIT} bit set.
942      */
showSoftInput(View view, int flags)943     public boolean showSoftInput(View view, int flags) {
944         return showSoftInput(view, flags, null);
945     }
946 
947     /**
948      * Flag for the {@link ResultReceiver} result code from
949      * {@link #showSoftInput(View, int, ResultReceiver)} and
950      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
951      * state of the soft input window was unchanged and remains shown.
952      */
953     public static final int RESULT_UNCHANGED_SHOWN = 0;
954 
955     /**
956      * Flag for the {@link ResultReceiver} result code from
957      * {@link #showSoftInput(View, int, ResultReceiver)} and
958      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
959      * state of the soft input window was unchanged and remains hidden.
960      */
961     public static final int RESULT_UNCHANGED_HIDDEN = 1;
962 
963     /**
964      * Flag for the {@link ResultReceiver} result code from
965      * {@link #showSoftInput(View, int, ResultReceiver)} and
966      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
967      * state of the soft input window changed from hidden to shown.
968      */
969     public static final int RESULT_SHOWN = 2;
970 
971     /**
972      * Flag for the {@link ResultReceiver} result code from
973      * {@link #showSoftInput(View, int, ResultReceiver)} and
974      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
975      * state of the soft input window changed from shown to hidden.
976      */
977     public static final int RESULT_HIDDEN = 3;
978 
979     /**
980      * Explicitly request that the current input method's soft input area be
981      * shown to the user, if needed.  Call this if the user interacts with
982      * your view in such a way that they have expressed they would like to
983      * start performing input into it.
984      *
985      * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to
986      * this method can be a long-lived object, because it may not be
987      * garbage-collected until all the corresponding {@link ResultReceiver}
988      * objects transferred to different processes get garbage-collected.
989      * Follow the general patterns to avoid memory leaks in Android.
990      * Consider to use {@link java.lang.ref.WeakReference} so that application
991      * logic objects such as {@link android.app.Activity} and {@link Context}
992      * can be garbage collected regardless of the lifetime of
993      * {@link ResultReceiver}.
994      *
995      * @param view The currently focused view, which would like to receive
996      * soft keyboard input.
997      * @param flags Provides additional operating flags.  Currently may be
998      * 0 or have the {@link #SHOW_IMPLICIT} bit set.
999      * @param resultReceiver If non-null, this will be called by the IME when
1000      * it has processed your request to tell you what it has done.  The result
1001      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
1002      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
1003      * {@link #RESULT_HIDDEN}.
1004      */
showSoftInput(View view, int flags, ResultReceiver resultReceiver)1005     public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
1006         checkFocus();
1007         synchronized (mH) {
1008             if (mServedView != view && (mServedView == null
1009                     || !mServedView.checkInputConnectionProxy(view))) {
1010                 return false;
1011             }
1012 
1013             try {
1014                 return mService.showSoftInput(mClient, flags, resultReceiver);
1015             } catch (RemoteException e) {
1016                 throw e.rethrowFromSystemServer();
1017             }
1018         }
1019     }
1020 
1021     /**
1022      * This method is still kept for a while until android.support.v7.widget.SearchView ver. 26.0
1023      * is publicly released because previous implementations of that class had relied on this method
1024      * via reflection.
1025      *
1026      * @deprecated This is a hidden API. You should never use this.
1027      * @hide
1028      */
1029     @Deprecated
showSoftInputUnchecked(int flags, ResultReceiver resultReceiver)1030     public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
1031         try {
1032             Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be removed "
1033                     + "soon. If you are using android.support.v7.widget.SearchView, please update "
1034                     + "to version 26.0 or newer version.");
1035             mService.showSoftInput(mClient, flags, resultReceiver);
1036         } catch (RemoteException e) {
1037             throw e.rethrowFromSystemServer();
1038         }
1039     }
1040 
1041     /**
1042      * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
1043      * input window should only be hidden if it was not explicitly shown
1044      * by the user.
1045      */
1046     public static final int HIDE_IMPLICIT_ONLY = 0x0001;
1047 
1048     /**
1049      * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
1050      * input window should normally be hidden, unless it was originally
1051      * shown with {@link #SHOW_FORCED}.
1052      */
1053     public static final int HIDE_NOT_ALWAYS = 0x0002;
1054 
1055     /**
1056      * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}
1057      * without a result: request to hide the soft input window from the
1058      * context of the window that is currently accepting input.
1059      *
1060      * @param windowToken The token of the window that is making the request,
1061      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1062      * @param flags Provides additional operating flags.  Currently may be
1063      * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
1064      */
hideSoftInputFromWindow(IBinder windowToken, int flags)1065     public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
1066         return hideSoftInputFromWindow(windowToken, flags, null);
1067     }
1068 
1069     /**
1070      * Request to hide the soft input window from the context of the window
1071      * that is currently accepting input.  This should be called as a result
1072      * of the user doing some actually than fairly explicitly requests to
1073      * have the input window hidden.
1074      *
1075      * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to
1076      * this method can be a long-lived object, because it may not be
1077      * garbage-collected until all the corresponding {@link ResultReceiver}
1078      * objects transferred to different processes get garbage-collected.
1079      * Follow the general patterns to avoid memory leaks in Android.
1080      * Consider to use {@link java.lang.ref.WeakReference} so that application
1081      * logic objects such as {@link android.app.Activity} and {@link Context}
1082      * can be garbage collected regardless of the lifetime of
1083      * {@link ResultReceiver}.
1084      *
1085      * @param windowToken The token of the window that is making the request,
1086      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1087      * @param flags Provides additional operating flags.  Currently may be
1088      * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
1089      * @param resultReceiver If non-null, this will be called by the IME when
1090      * it has processed your request to tell you what it has done.  The result
1091      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
1092      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
1093      * {@link #RESULT_HIDDEN}.
1094      */
hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver)1095     public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
1096             ResultReceiver resultReceiver) {
1097         checkFocus();
1098         synchronized (mH) {
1099             if (mServedView == null || mServedView.getWindowToken() != windowToken) {
1100                 return false;
1101             }
1102 
1103             try {
1104                 return mService.hideSoftInput(mClient, flags, resultReceiver);
1105             } catch (RemoteException e) {
1106                 throw e.rethrowFromSystemServer();
1107             }
1108         }
1109     }
1110 
1111 
1112     /**
1113      * This method toggles the input method window display.
1114      * If the input window is already displayed, it gets hidden.
1115      * If not the input window will be displayed.
1116      * @param windowToken The token of the window that is making the request,
1117      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1118      * @param showFlags Provides additional operating flags.  May be
1119      * 0 or have the {@link #SHOW_IMPLICIT},
1120      * {@link #SHOW_FORCED} bit set.
1121      * @param hideFlags Provides additional operating flags.  May be
1122      * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1123      * {@link #HIDE_NOT_ALWAYS} bit set.
1124      **/
toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags)1125     public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
1126         synchronized (mH) {
1127             if (mServedView == null || mServedView.getWindowToken() != windowToken) {
1128                 return;
1129             }
1130             if (mCurMethod != null) {
1131                 try {
1132                     mCurMethod.toggleSoftInput(showFlags, hideFlags);
1133                 } catch (RemoteException e) {
1134                 }
1135             }
1136         }
1137     }
1138 
1139     /*
1140      * This method toggles the input method window display.
1141      * If the input window is already displayed, it gets hidden.
1142      * If not the input window will be displayed.
1143      * @param showFlags Provides additional operating flags.  May be
1144      * 0 or have the {@link #SHOW_IMPLICIT},
1145      * {@link #SHOW_FORCED} bit set.
1146      * @param hideFlags Provides additional operating flags.  May be
1147      * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1148      * {@link #HIDE_NOT_ALWAYS} bit set.
1149      * @hide
1150      */
toggleSoftInput(int showFlags, int hideFlags)1151     public void toggleSoftInput(int showFlags, int hideFlags) {
1152         if (mCurMethod != null) {
1153             try {
1154                 mCurMethod.toggleSoftInput(showFlags, hideFlags);
1155             } catch (RemoteException e) {
1156             }
1157         }
1158     }
1159 
1160     /**
1161      * If the input method is currently connected to the given view,
1162      * restart it with its new contents.  You should call this when the text
1163      * within your view changes outside of the normal input method or key
1164      * input flow, such as when an application calls TextView.setText().
1165      *
1166      * @param view The view whose text has changed.
1167      */
restartInput(View view)1168     public void restartInput(View view) {
1169         checkFocus();
1170         synchronized (mH) {
1171             if (mServedView != view && (mServedView == null
1172                     || !mServedView.checkInputConnectionProxy(view))) {
1173                 return;
1174             }
1175 
1176             mServedConnecting = true;
1177         }
1178 
1179         startInputInner(InputMethodClient.START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, null, 0,
1180                 0, 0);
1181     }
1182 
startInputInner(@nputMethodClient.StartInputReason final int startInputReason, IBinder windowGainingFocus, int controlFlags, int softInputMode, int windowFlags)1183     boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
1184             IBinder windowGainingFocus, int controlFlags, int softInputMode,
1185             int windowFlags) {
1186         final View view;
1187         synchronized (mH) {
1188             view = mServedView;
1189 
1190             // Make sure we have a window token for the served view.
1191             if (DEBUG) {
1192                 Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
1193                         " reason=" + InputMethodClient.getStartInputReason(startInputReason));
1194             }
1195             if (view == null) {
1196                 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
1197                 return false;
1198             }
1199         }
1200 
1201         // Now we need to get an input connection from the served view.
1202         // This is complicated in a couple ways: we can't be holding our lock
1203         // when calling out to the view, and we need to make sure we call into
1204         // the view on the same thread that is driving its view hierarchy.
1205         Handler vh = view.getHandler();
1206         if (vh == null) {
1207             // If the view doesn't have a handler, something has changed out
1208             // from under us, so just close the current input.
1209             // If we don't close the current input, the current input method can remain on the
1210             // screen without a connection.
1211             if (DEBUG) Log.v(TAG, "ABORT input: no handler for view! Close current input.");
1212             closeCurrentInput();
1213             return false;
1214         }
1215         if (vh.getLooper() != Looper.myLooper()) {
1216             // The view is running on a different thread than our own, so
1217             // we need to reschedule our work for over there.
1218             if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
1219             vh.post(new Runnable() {
1220                 @Override
1221                 public void run() {
1222                     startInputInner(startInputReason, null, 0, 0, 0);
1223                 }
1224             });
1225             return false;
1226         }
1227 
1228         // Okay we are now ready to call into the served view and have it
1229         // do its stuff.
1230         // Life is good: let's hook everything up!
1231         EditorInfo tba = new EditorInfo();
1232         // Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the
1233         // system can verify the consistency between the uid of this process and package name passed
1234         // from here. See comment of Context#getOpPackageName() for details.
1235         tba.packageName = view.getContext().getOpPackageName();
1236         tba.fieldId = view.getId();
1237         InputConnection ic = view.onCreateInputConnection(tba);
1238         if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
1239 
1240         synchronized (mH) {
1241             // Now that we are locked again, validate that our state hasn't
1242             // changed.
1243             if (mServedView != view || !mServedConnecting) {
1244                 // Something else happened, so abort.
1245                 if (DEBUG) Log.v(TAG,
1246                         "Starting input: finished by someone else. view=" + dumpViewInfo(view)
1247                         + " mServedView=" + dumpViewInfo(mServedView)
1248                         + " mServedConnecting=" + mServedConnecting);
1249                 return false;
1250             }
1251 
1252             // If we already have a text box, then this view is already
1253             // connected so we want to restart it.
1254             if (mCurrentTextBoxAttribute == null) {
1255                 controlFlags |= CONTROL_START_INITIAL;
1256             }
1257 
1258             // Hook 'em up and let 'er rip.
1259             mCurrentTextBoxAttribute = tba;
1260             mServedConnecting = false;
1261             if (mServedInputConnectionWrapper != null) {
1262                 mServedInputConnectionWrapper.deactivate();
1263                 mServedInputConnectionWrapper = null;
1264             }
1265             ControlledInputConnectionWrapper servedContext;
1266             final int missingMethodFlags;
1267             if (ic != null) {
1268                 mCursorSelStart = tba.initialSelStart;
1269                 mCursorSelEnd = tba.initialSelEnd;
1270                 mCursorCandStart = -1;
1271                 mCursorCandEnd = -1;
1272                 mCursorRect.setEmpty();
1273                 mCursorAnchorInfo = null;
1274                 final Handler icHandler;
1275                 missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic);
1276                 if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER)
1277                         != 0) {
1278                     // InputConnection#getHandler() is not implemented.
1279                     icHandler = null;
1280                 } else {
1281                     icHandler = ic.getHandler();
1282                 }
1283                 servedContext = new ControlledInputConnectionWrapper(
1284                         icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this);
1285             } else {
1286                 servedContext = null;
1287                 missingMethodFlags = 0;
1288             }
1289             mServedInputConnectionWrapper = servedContext;
1290 
1291             try {
1292                 if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
1293                         + ic + " tba=" + tba + " controlFlags=#"
1294                         + Integer.toHexString(controlFlags));
1295                 final InputBindResult res = mService.startInputOrWindowGainedFocus(
1296                         startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode,
1297                         windowFlags, tba, servedContext, missingMethodFlags);
1298                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1299                 if (res != null) {
1300                     if (res.id != null) {
1301                         setInputChannelLocked(res.channel);
1302                         mBindSequence = res.sequence;
1303                         mCurMethod = res.method;
1304                         mCurId = res.id;
1305                         mNextUserActionNotificationSequenceNumber =
1306                                 res.userActionNotificationSequenceNumber;
1307                     } else {
1308                         if (res.channel != null && res.channel != mCurChannel) {
1309                             res.channel.dispose();
1310                         }
1311                         if (mCurMethod == null) {
1312                             // This means there is no input method available.
1313                             if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1314                             return true;
1315                         }
1316                     }
1317                 } else {
1318                     if (startInputReason
1319                             == InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN) {
1320                         // We are here probably because of an obsolete window-focus-in message sent
1321                         // to windowGainingFocus.  Since IMMS determines whether a Window can have
1322                         // IME focus or not by using the latest window focus state maintained in the
1323                         // WMS, this kind of race condition cannot be avoided.  One obvious example
1324                         // would be that we have already received a window-focus-out message but the
1325                         // UI thread is still handling previous window-focus-in message here.
1326                         // TODO: InputBindResult should have the error code.
1327                         if (DEBUG) Log.w(TAG, "startInputOrWindowGainedFocus failed. "
1328                                 + "Window focus may have already been lost. "
1329                                 + "win=" + windowGainingFocus + " view=" + dumpViewInfo(view));
1330                         if (!mActive) {
1331                             // mHasBeenInactive is a latch switch to forcefully refresh IME focus
1332                             // state when an inactive (mActive == false) client is gaining window
1333                             // focus. In case we have unnecessary disable the latch due to this
1334                             // spurious wakeup, we re-enable the latch here.
1335                             // TODO: Come up with more robust solution.
1336                             mHasBeenInactive = true;
1337                         }
1338                     }
1339                 }
1340                 if (mCurMethod != null && mCompletions != null) {
1341                     try {
1342                         mCurMethod.displayCompletions(mCompletions);
1343                     } catch (RemoteException e) {
1344                     }
1345                 }
1346             } catch (RemoteException e) {
1347                 Log.w(TAG, "IME died: " + mCurId, e);
1348             }
1349         }
1350 
1351         return true;
1352     }
1353 
1354     /**
1355      * When the focused window is dismissed, this method is called to finish the
1356      * input method started before.
1357      * @hide
1358      */
windowDismissed(IBinder appWindowToken)1359     public void windowDismissed(IBinder appWindowToken) {
1360         checkFocus();
1361         synchronized (mH) {
1362             if (mServedView != null &&
1363                     mServedView.getWindowToken() == appWindowToken) {
1364                 finishInputLocked();
1365             }
1366         }
1367     }
1368 
1369     /**
1370      * Call this when a view receives focus.
1371      * @hide
1372      */
focusIn(View view)1373     public void focusIn(View view) {
1374         synchronized (mH) {
1375             focusInLocked(view);
1376         }
1377     }
1378 
focusInLocked(View view)1379     void focusInLocked(View view) {
1380         if (DEBUG) Log.v(TAG, "focusIn: " + dumpViewInfo(view));
1381 
1382         if (view != null && view.isTemporarilyDetached()) {
1383             // This is a request from a view that is temporarily detached from a window.
1384             if (DEBUG) Log.v(TAG, "Temporarily detached view, ignoring");
1385             return;
1386         }
1387 
1388         if (mCurRootView != view.getRootView()) {
1389             // This is a request from a window that isn't in the window with
1390             // IME focus, so ignore it.
1391             if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1392             return;
1393         }
1394 
1395         mNextServedView = view;
1396         scheduleCheckFocusLocked(view);
1397     }
1398 
1399     /**
1400      * Call this when a view loses focus.
1401      * @hide
1402      */
focusOut(View view)1403     public void focusOut(View view) {
1404         synchronized (mH) {
1405             if (DEBUG) Log.v(TAG, "focusOut: view=" + dumpViewInfo(view)
1406                     + " mServedView=" + dumpViewInfo(mServedView));
1407             if (mServedView != view) {
1408                 // The following code would auto-hide the IME if we end up
1409                 // with no more views with focus.  This can happen, however,
1410                 // whenever we go into touch mode, so it ends up hiding
1411                 // at times when we don't really want it to.  For now it
1412                 // seems better to just turn it all off.
1413                 // TODO: Check view.isTemporarilyDetached() when re-enable the following code.
1414                 if (false && view.hasWindowFocus()) {
1415                     mNextServedView = null;
1416                     scheduleCheckFocusLocked(view);
1417                 }
1418             }
1419         }
1420     }
1421 
1422     /**
1423      * Call this when a view is being detached from a {@link android.view.Window}.
1424      * @hide
1425      */
onViewDetachedFromWindow(View view)1426     public void onViewDetachedFromWindow(View view) {
1427         synchronized (mH) {
1428             if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: view=" + dumpViewInfo(view)
1429                     + " mServedView=" + dumpViewInfo(mServedView));
1430             if (mServedView == view) {
1431                 mNextServedView = null;
1432                 scheduleCheckFocusLocked(view);
1433             }
1434         }
1435     }
1436 
scheduleCheckFocusLocked(View view)1437     static void scheduleCheckFocusLocked(View view) {
1438         ViewRootImpl viewRootImpl = view.getViewRootImpl();
1439         if (viewRootImpl != null) {
1440             viewRootImpl.dispatchCheckFocus();
1441         }
1442     }
1443 
1444     /**
1445      * @hide
1446      */
checkFocus()1447     public void checkFocus() {
1448         if (checkFocusNoStartInput(false)) {
1449             startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
1450         }
1451     }
1452 
checkFocusNoStartInput(boolean forceNewFocus)1453     private boolean checkFocusNoStartInput(boolean forceNewFocus) {
1454         // This is called a lot, so short-circuit before locking.
1455         if (mServedView == mNextServedView && !forceNewFocus) {
1456             return false;
1457         }
1458 
1459         final ControlledInputConnectionWrapper ic;
1460         synchronized (mH) {
1461             if (mServedView == mNextServedView && !forceNewFocus) {
1462                 return false;
1463             }
1464             if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1465                     + " next=" + mNextServedView
1466                     + " forceNewFocus=" + forceNewFocus
1467                     + " package="
1468                     + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
1469 
1470             if (mNextServedView == null) {
1471                 finishInputLocked();
1472                 // In this case, we used to have a focused view on the window,
1473                 // but no longer do.  We should make sure the input method is
1474                 // no longer shown, since it serves no purpose.
1475                 closeCurrentInput();
1476                 return false;
1477             }
1478 
1479             ic = mServedInputConnectionWrapper;
1480 
1481             mServedView = mNextServedView;
1482             mCurrentTextBoxAttribute = null;
1483             mCompletions = null;
1484             mServedConnecting = true;
1485         }
1486 
1487         if (ic != null) {
1488             ic.finishComposingText();
1489         }
1490 
1491         return true;
1492     }
1493 
closeCurrentInput()1494     void closeCurrentInput() {
1495         try {
1496             mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
1497         } catch (RemoteException e) {
1498             throw e.rethrowFromSystemServer();
1499         }
1500     }
1501 
1502     /**
1503      * Called by ViewAncestor when its window gets input focus.
1504      * @hide
1505      */
onPostWindowFocus(View rootView, View focusedView, @SoftInputModeFlags int softInputMode, boolean first, int windowFlags)1506     public void onPostWindowFocus(View rootView, View focusedView,
1507             @SoftInputModeFlags int softInputMode, boolean first, int windowFlags) {
1508         boolean forceNewFocus = false;
1509         synchronized (mH) {
1510             if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1511                     + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
1512                     + " first=" + first + " flags=#"
1513                     + Integer.toHexString(windowFlags));
1514             if (mHasBeenInactive) {
1515                 if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
1516                 mHasBeenInactive = false;
1517                 forceNewFocus = true;
1518             }
1519             focusInLocked(focusedView != null ? focusedView : rootView);
1520         }
1521 
1522         int controlFlags = 0;
1523         if (focusedView != null) {
1524             controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
1525             if (focusedView.onCheckIsTextEditor()) {
1526                 controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
1527             }
1528         }
1529         if (first) {
1530             controlFlags |= CONTROL_WINDOW_FIRST;
1531         }
1532 
1533         if (checkFocusNoStartInput(forceNewFocus)) {
1534             // We need to restart input on the current focus view.  This
1535             // should be done in conjunction with telling the system service
1536             // about the window gaining focus, to help make the transition
1537             // smooth.
1538             if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN,
1539                     rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) {
1540                 return;
1541             }
1542         }
1543 
1544         // For some reason we didn't do a startInput + windowFocusGain, so
1545         // we'll just do a window focus gain and call it a day.
1546         synchronized (mH) {
1547             try {
1548                 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
1549                 mService.startInputOrWindowGainedFocus(
1550                         InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
1551                         rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
1552                         null, 0 /* missingMethodFlags */);
1553             } catch (RemoteException e) {
1554                 throw e.rethrowFromSystemServer();
1555             }
1556         }
1557     }
1558 
1559     /** @hide */
onPreWindowFocus(View rootView, boolean hasWindowFocus)1560     public void onPreWindowFocus(View rootView, boolean hasWindowFocus) {
1561         synchronized (mH) {
1562             if (rootView == null) {
1563                 mCurRootView = null;
1564             } if (hasWindowFocus) {
1565                 mCurRootView = rootView;
1566             } else if (rootView == mCurRootView) {
1567                 // If the mCurRootView is losing window focus, release the strong reference to it
1568                 // so as not to prevent it from being garbage-collected.
1569                 mCurRootView = null;
1570             } else {
1571                 if (DEBUG) {
1572                     Log.v(TAG, "Ignoring onPreWindowFocus()."
1573                             + " mCurRootView=" + mCurRootView + " rootView=" + rootView);
1574                 }
1575             }
1576         }
1577     }
1578 
1579     /**
1580      * Report the current selection range.
1581      *
1582      * <p><strong>Editor authors</strong>, you need to call this method whenever
1583      * the cursor moves in your editor. Remember that in addition to doing this, your
1584      * editor needs to always supply current cursor values in
1585      * {@link EditorInfo#initialSelStart} and {@link EditorInfo#initialSelEnd} every
1586      * time {@link android.view.View#onCreateInputConnection(EditorInfo)} is
1587      * called, which happens whenever the keyboard shows up or the focus changes
1588      * to a text field, among other cases.</p>
1589      */
updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd)1590     public void updateSelection(View view, int selStart, int selEnd,
1591             int candidatesStart, int candidatesEnd) {
1592         checkFocus();
1593         synchronized (mH) {
1594             if ((mServedView != view && (mServedView == null
1595                         || !mServedView.checkInputConnectionProxy(view)))
1596                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1597                 return;
1598             }
1599 
1600             if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1601                     || mCursorCandStart != candidatesStart
1602                     || mCursorCandEnd != candidatesEnd) {
1603                 if (DEBUG) Log.d(TAG, "updateSelection");
1604 
1605                 try {
1606                     if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
1607                     final int oldSelStart = mCursorSelStart;
1608                     final int oldSelEnd = mCursorSelEnd;
1609                     // Update internal values before sending updateSelection to the IME, because
1610                     // if it changes the text within its onUpdateSelection handler in a way that
1611                     // does not move the cursor we don't want to call it again with the same values.
1612                     mCursorSelStart = selStart;
1613                     mCursorSelEnd = selEnd;
1614                     mCursorCandStart = candidatesStart;
1615                     mCursorCandEnd = candidatesEnd;
1616                     mCurMethod.updateSelection(oldSelStart, oldSelEnd,
1617                             selStart, selEnd, candidatesStart, candidatesEnd);
1618                 } catch (RemoteException e) {
1619                     Log.w(TAG, "IME died: " + mCurId, e);
1620                 }
1621             }
1622         }
1623     }
1624 
1625     /**
1626      * Notify the event when the user tapped or clicked the text view.
1627      */
viewClicked(View view)1628     public void viewClicked(View view) {
1629         final boolean focusChanged = mServedView != mNextServedView;
1630         checkFocus();
1631         synchronized (mH) {
1632             if ((mServedView != view && (mServedView == null
1633                     || !mServedView.checkInputConnectionProxy(view)))
1634                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1635                 return;
1636             }
1637             try {
1638                 if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
1639                 mCurMethod.viewClicked(focusChanged);
1640             } catch (RemoteException e) {
1641                 Log.w(TAG, "IME died: " + mCurId, e);
1642             }
1643         }
1644     }
1645 
1646     /**
1647      * Return true if the current input method wants to watch the location
1648      * of the input editor's cursor in its window.
1649      *
1650      * @deprecated Use {@link InputConnection#requestCursorUpdates(int)} instead.
1651      */
1652     @Deprecated
isWatchingCursor(View view)1653     public boolean isWatchingCursor(View view) {
1654         return false;
1655     }
1656 
1657     /**
1658      * Return true if the current input method wants to be notified when cursor/anchor location
1659      * is changed.
1660      *
1661      * @hide
1662      */
isCursorAnchorInfoEnabled()1663     public boolean isCursorAnchorInfoEnabled() {
1664         synchronized (mH) {
1665             final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode &
1666                     InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0;
1667             final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode &
1668                     InputConnection.CURSOR_UPDATE_MONITOR) != 0;
1669             return isImmediate || isMonitoring;
1670         }
1671     }
1672 
1673     /**
1674      * Set the requested mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}.
1675      *
1676      * @hide
1677      */
setUpdateCursorAnchorInfoMode(int flags)1678     public void setUpdateCursorAnchorInfoMode(int flags) {
1679         synchronized (mH) {
1680             mRequestUpdateCursorAnchorInfoMonitorMode = flags;
1681         }
1682     }
1683 
1684     /**
1685      * Report the current cursor location in its window.
1686      *
1687      * @deprecated Use {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} instead.
1688      */
1689     @Deprecated
updateCursor(View view, int left, int top, int right, int bottom)1690     public void updateCursor(View view, int left, int top, int right, int bottom) {
1691         checkFocus();
1692         synchronized (mH) {
1693             if ((mServedView != view && (mServedView == null
1694                         || !mServedView.checkInputConnectionProxy(view)))
1695                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1696                 return;
1697             }
1698 
1699             mTmpCursorRect.set(left, top, right, bottom);
1700             if (!mCursorRect.equals(mTmpCursorRect)) {
1701                 if (DEBUG) Log.d(TAG, "updateCursor");
1702 
1703                 try {
1704                     if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1705                     mCurMethod.updateCursor(mTmpCursorRect);
1706                     mCursorRect.set(mTmpCursorRect);
1707                 } catch (RemoteException e) {
1708                     Log.w(TAG, "IME died: " + mCurId, e);
1709                 }
1710             }
1711         }
1712     }
1713 
1714     /**
1715      * Report positional change of the text insertion point and/or characters in the composition
1716      * string.
1717      */
updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo)1718     public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) {
1719         if (view == null || cursorAnchorInfo == null) {
1720             return;
1721         }
1722         checkFocus();
1723         synchronized (mH) {
1724             if ((mServedView != view &&
1725                     (mServedView == null || !mServedView.checkInputConnectionProxy(view)))
1726                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1727                 return;
1728             }
1729             // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
1730             // not been changed from the previous call.
1731             final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode &
1732                     InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0;
1733             if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) {
1734                 // TODO: Consider always emitting this message once we have addressed redundant
1735                 // calls of this method from android.widget.Editor.
1736                 if (DEBUG) {
1737                     Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info="
1738                             + cursorAnchorInfo);
1739                 }
1740                 return;
1741             }
1742             if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
1743             try {
1744                 mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
1745                 mCursorAnchorInfo = cursorAnchorInfo;
1746                 // Clear immediate bit (if any).
1747                 mRequestUpdateCursorAnchorInfoMonitorMode &=
1748                         ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
1749             } catch (RemoteException e) {
1750                 Log.w(TAG, "IME died: " + mCurId, e);
1751             }
1752         }
1753     }
1754 
1755     /**
1756      * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1757      * InputMethodSession.appPrivateCommand()} on the current Input Method.
1758      * @param view Optional View that is sending the command, or null if
1759      * you want to send the command regardless of the view that is attached
1760      * to the input method.
1761      * @param action Name of the command to be performed.  This <em>must</em>
1762      * be a scoped name, i.e. prefixed with a package name you own, so that
1763      * different developers will not create conflicting commands.
1764      * @param data Any data to include with the command.
1765      */
sendAppPrivateCommand(View view, String action, Bundle data)1766     public void sendAppPrivateCommand(View view, String action, Bundle data) {
1767         checkFocus();
1768         synchronized (mH) {
1769             if ((mServedView != view && (mServedView == null
1770                         || !mServedView.checkInputConnectionProxy(view)))
1771                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1772                 return;
1773             }
1774             try {
1775                 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1776                 mCurMethod.appPrivateCommand(action, data);
1777             } catch (RemoteException e) {
1778                 Log.w(TAG, "IME died: " + mCurId, e);
1779             }
1780         }
1781     }
1782 
1783     /**
1784      * Force switch to a new input method component. This can only be called
1785      * from an application or a service which has a token of the currently active input method.
1786      * @param token Supplies the identifying token given to an input method
1787      * when it was started, which allows it to perform this operation on
1788      * itself.
1789      * @param id The unique identifier for the new input method to be switched to.
1790      */
setInputMethod(IBinder token, String id)1791     public void setInputMethod(IBinder token, String id) {
1792         try {
1793             mService.setInputMethod(token, id);
1794         } catch (RemoteException e) {
1795             throw e.rethrowFromSystemServer();
1796         }
1797     }
1798 
1799     /**
1800      * Force switch to a new input method and subtype. This can only be called
1801      * from an application or a service which has a token of the currently active input method.
1802      * @param token Supplies the identifying token given to an input method
1803      * when it was started, which allows it to perform this operation on
1804      * itself.
1805      * @param id The unique identifier for the new input method to be switched to.
1806      * @param subtype The new subtype of the new input method to be switched to.
1807      */
setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype)1808     public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
1809         try {
1810             mService.setInputMethodAndSubtype(token, id, subtype);
1811         } catch (RemoteException e) {
1812             throw e.rethrowFromSystemServer();
1813         }
1814     }
1815 
1816     /**
1817      * Close/hide the input method's soft input area, so the user no longer
1818      * sees it or can interact with it.  This can only be called
1819      * from the currently active input method, as validated by the given token.
1820      *
1821      * @param token Supplies the identifying token given to an input method
1822      * when it was started, which allows it to perform this operation on
1823      * itself.
1824      * @param flags Provides additional operating flags.  Currently may be
1825      * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1826      * {@link #HIDE_NOT_ALWAYS} bit set.
1827      */
hideSoftInputFromInputMethod(IBinder token, int flags)1828     public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1829         try {
1830             mService.hideMySoftInput(token, flags);
1831         } catch (RemoteException e) {
1832             throw e.rethrowFromSystemServer();
1833         }
1834     }
1835 
1836     /**
1837      * Show the input method's soft input area, so the user
1838      * sees the input method window and can interact with it.
1839      * This can only be called from the currently active input method,
1840      * as validated by the given token.
1841      *
1842      * @param token Supplies the identifying token given to an input method
1843      * when it was started, which allows it to perform this operation on
1844      * itself.
1845      * @param flags Provides additional operating flags.  Currently may be
1846      * 0 or have the {@link #SHOW_IMPLICIT} or
1847      * {@link #SHOW_FORCED} bit set.
1848      */
showSoftInputFromInputMethod(IBinder token, int flags)1849     public void showSoftInputFromInputMethod(IBinder token, int flags) {
1850         try {
1851             mService.showMySoftInput(token, flags);
1852         } catch (RemoteException e) {
1853             throw e.rethrowFromSystemServer();
1854         }
1855     }
1856 
1857     /**
1858      * Dispatches an input event to the IME.
1859      *
1860      * Returns {@link #DISPATCH_HANDLED} if the event was handled.
1861      * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
1862      * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
1863      * callback will be invoked later.
1864      *
1865      * @hide
1866      */
dispatchInputEvent(InputEvent event, Object token, FinishedInputEventCallback callback, Handler handler)1867     public int dispatchInputEvent(InputEvent event, Object token,
1868             FinishedInputEventCallback callback, Handler handler) {
1869         synchronized (mH) {
1870             if (mCurMethod != null) {
1871                 if (event instanceof KeyEvent) {
1872                     KeyEvent keyEvent = (KeyEvent)event;
1873                     if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
1874                             && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
1875                             && keyEvent.getRepeatCount() == 0) {
1876                         showInputMethodPickerLocked();
1877                         return DISPATCH_HANDLED;
1878                     }
1879                 }
1880 
1881                 if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
1882 
1883                 PendingEvent p = obtainPendingEventLocked(
1884                         event, token, mCurId, callback, handler);
1885                 if (mMainLooper.isCurrentThread()) {
1886                     // Already running on the IMM thread so we can send the event immediately.
1887                     return sendInputEventOnMainLooperLocked(p);
1888                 }
1889 
1890                 // Post the event to the IMM thread.
1891                 Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
1892                 msg.setAsynchronous(true);
1893                 mH.sendMessage(msg);
1894                 return DISPATCH_IN_PROGRESS;
1895             }
1896         }
1897         return DISPATCH_NOT_HANDLED;
1898     }
1899 
1900     /**
1901      * Provides the default implementation of {@link InputConnection#sendKeyEvent(KeyEvent)}, which
1902      * is expected to dispatch an keyboard event sent from the IME to an appropriate event target
1903      * depending on the given {@link View} and the current focus state.
1904      *
1905      * <p>CAUTION: This method is provided only for the situation where
1906      * {@link InputConnection#sendKeyEvent(KeyEvent)} needs to be implemented without relying on
1907      * {@link BaseInputConnection}. Do not use this API for anything else.</p>
1908      *
1909      * @param targetView the default target view. If {@code null} is specified, then this method
1910      * tries to find a good event target based on the current focus state.
1911      * @param event the key event to be dispatched.
1912      */
dispatchKeyEventFromInputMethod(@ullable View targetView, @NonNull KeyEvent event)1913     public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
1914             @NonNull KeyEvent event) {
1915         synchronized (mH) {
1916             ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
1917             if (viewRootImpl == null) {
1918                 if (mServedView != null) {
1919                     viewRootImpl = mServedView.getViewRootImpl();
1920                 }
1921             }
1922             if (viewRootImpl != null) {
1923                 viewRootImpl.dispatchKeyFromIme(event);
1924             }
1925         }
1926     }
1927 
1928     // Must be called on the main looper
sendInputEventAndReportResultOnMainLooper(PendingEvent p)1929     void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
1930         final boolean handled;
1931         synchronized (mH) {
1932             int result = sendInputEventOnMainLooperLocked(p);
1933             if (result == DISPATCH_IN_PROGRESS) {
1934                 return;
1935             }
1936 
1937             handled = (result == DISPATCH_HANDLED);
1938         }
1939 
1940         invokeFinishedInputEventCallback(p, handled);
1941     }
1942 
1943     // Must be called on the main looper
sendInputEventOnMainLooperLocked(PendingEvent p)1944     int sendInputEventOnMainLooperLocked(PendingEvent p) {
1945         if (mCurChannel != null) {
1946             if (mCurSender == null) {
1947                 mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
1948             }
1949 
1950             final InputEvent event = p.mEvent;
1951             final int seq = event.getSequenceNumber();
1952             if (mCurSender.sendInputEvent(seq, event)) {
1953                 mPendingEvents.put(seq, p);
1954                 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
1955                         mPendingEvents.size());
1956 
1957                 Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, seq, 0, p);
1958                 msg.setAsynchronous(true);
1959                 mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
1960                 return DISPATCH_IN_PROGRESS;
1961             }
1962 
1963             Log.w(TAG, "Unable to send input event to IME: "
1964                     + mCurId + " dropping: " + event);
1965         }
1966         return DISPATCH_NOT_HANDLED;
1967     }
1968 
finishedInputEvent(int seq, boolean handled, boolean timeout)1969     void finishedInputEvent(int seq, boolean handled, boolean timeout) {
1970         final PendingEvent p;
1971         synchronized (mH) {
1972             int index = mPendingEvents.indexOfKey(seq);
1973             if (index < 0) {
1974                 return; // spurious, event already finished or timed out
1975             }
1976 
1977             p = mPendingEvents.valueAt(index);
1978             mPendingEvents.removeAt(index);
1979             Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
1980 
1981             if (timeout) {
1982                 Log.w(TAG, "Timeout waiting for IME to handle input event after "
1983                         + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
1984             } else {
1985                 mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
1986             }
1987         }
1988 
1989         invokeFinishedInputEventCallback(p, handled);
1990     }
1991 
1992     // Assumes the event has already been removed from the queue.
invokeFinishedInputEventCallback(PendingEvent p, boolean handled)1993     void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
1994         p.mHandled = handled;
1995         if (p.mHandler.getLooper().isCurrentThread()) {
1996             // Already running on the callback handler thread so we can send the
1997             // callback immediately.
1998             p.run();
1999         } else {
2000             // Post the event to the callback handler thread.
2001             // In this case, the callback will be responsible for recycling the event.
2002             Message msg = Message.obtain(p.mHandler, p);
2003             msg.setAsynchronous(true);
2004             msg.sendToTarget();
2005         }
2006     }
2007 
flushPendingEventsLocked()2008     private void flushPendingEventsLocked() {
2009         mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
2010 
2011         final int count = mPendingEvents.size();
2012         for (int i = 0; i < count; i++) {
2013             int seq = mPendingEvents.keyAt(i);
2014             Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
2015             msg.setAsynchronous(true);
2016             msg.sendToTarget();
2017         }
2018     }
2019 
obtainPendingEventLocked(InputEvent event, Object token, String inputMethodId, FinishedInputEventCallback callback, Handler handler)2020     private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
2021             String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
2022         PendingEvent p = mPendingEventPool.acquire();
2023         if (p == null) {
2024             p = new PendingEvent();
2025         }
2026         p.mEvent = event;
2027         p.mToken = token;
2028         p.mInputMethodId = inputMethodId;
2029         p.mCallback = callback;
2030         p.mHandler = handler;
2031         return p;
2032     }
2033 
recyclePendingEventLocked(PendingEvent p)2034     private void recyclePendingEventLocked(PendingEvent p) {
2035         p.recycle();
2036         mPendingEventPool.release(p);
2037     }
2038 
showInputMethodPicker()2039     public void showInputMethodPicker() {
2040         synchronized (mH) {
2041             showInputMethodPickerLocked();
2042         }
2043     }
2044 
2045     /**
2046      * Shows the input method chooser dialog.
2047      *
2048      * @param showAuxiliarySubtypes Set true to show auxiliary input methods.
2049      * @hide
2050      */
showInputMethodPicker(boolean showAuxiliarySubtypes)2051     public void showInputMethodPicker(boolean showAuxiliarySubtypes) {
2052         synchronized (mH) {
2053             try {
2054                 final int mode = showAuxiliarySubtypes ?
2055                         SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
2056                         SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
2057                 mService.showInputMethodPickerFromClient(mClient, mode);
2058             } catch (RemoteException e) {
2059                 throw e.rethrowFromSystemServer();
2060             }
2061         }
2062     }
2063 
showInputMethodPickerLocked()2064     private void showInputMethodPickerLocked() {
2065         try {
2066             mService.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO);
2067         } catch (RemoteException e) {
2068             throw e.rethrowFromSystemServer();
2069         }
2070     }
2071 
2072     /**
2073      * Show the settings for enabling subtypes of the specified input method.
2074      * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
2075      * subtypes of all input methods will be shown.
2076      */
showInputMethodAndSubtypeEnabler(String imiId)2077     public void showInputMethodAndSubtypeEnabler(String imiId) {
2078         synchronized (mH) {
2079             try {
2080                 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
2081             } catch (RemoteException e) {
2082                 throw e.rethrowFromSystemServer();
2083             }
2084         }
2085     }
2086 
2087     /**
2088      * Returns the current input method subtype. This subtype is one of the subtypes in
2089      * the current input method. This method returns null when the current input method doesn't
2090      * have any input method subtype.
2091      */
getCurrentInputMethodSubtype()2092     public InputMethodSubtype getCurrentInputMethodSubtype() {
2093         try {
2094             return mService.getCurrentInputMethodSubtype();
2095         } catch (RemoteException e) {
2096             throw e.rethrowFromSystemServer();
2097         }
2098     }
2099 
2100     /**
2101      * Switch to a new input method subtype of the current input method.
2102      * @param subtype A new input method subtype to switch.
2103      * @return true if the current subtype was successfully switched. When the specified subtype is
2104      * null, this method returns false.
2105      */
2106     @RequiresPermission(WRITE_SECURE_SETTINGS)
setCurrentInputMethodSubtype(InputMethodSubtype subtype)2107     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
2108         synchronized (mH) {
2109             try {
2110                 return mService.setCurrentInputMethodSubtype(subtype);
2111             } catch (RemoteException e) {
2112                 throw e.rethrowFromSystemServer();
2113             }
2114         }
2115     }
2116 
2117     /**
2118      * Notify that a user took some action with this input method.
2119      * @hide
2120      */
notifyUserAction()2121     public void notifyUserAction() {
2122         synchronized (mH) {
2123             if (mLastSentUserActionNotificationSequenceNumber ==
2124                     mNextUserActionNotificationSequenceNumber) {
2125                 if (DEBUG) {
2126                     Log.w(TAG, "Ignoring notifyUserAction as it has already been sent."
2127                             + " mLastSentUserActionNotificationSequenceNumber: "
2128                             + mLastSentUserActionNotificationSequenceNumber
2129                             + " mNextUserActionNotificationSequenceNumber: "
2130                             + mNextUserActionNotificationSequenceNumber);
2131                 }
2132                 return;
2133             }
2134             try {
2135                 if (DEBUG) {
2136                     Log.w(TAG, "notifyUserAction: "
2137                             + " mLastSentUserActionNotificationSequenceNumber: "
2138                             + mLastSentUserActionNotificationSequenceNumber
2139                             + " mNextUserActionNotificationSequenceNumber: "
2140                             + mNextUserActionNotificationSequenceNumber);
2141                 }
2142                 mService.notifyUserAction(mNextUserActionNotificationSequenceNumber);
2143                 mLastSentUserActionNotificationSequenceNumber =
2144                         mNextUserActionNotificationSequenceNumber;
2145             } catch (RemoteException e) {
2146                 throw e.rethrowFromSystemServer();
2147             }
2148         }
2149     }
2150 
2151     /**
2152      * Returns a map of all shortcut input method info and their subtypes.
2153      */
getShortcutInputMethodsAndSubtypes()2154     public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
2155         synchronized (mH) {
2156             HashMap<InputMethodInfo, List<InputMethodSubtype>> ret = new HashMap<>();
2157             try {
2158                 // TODO: We should change the return type from List<Object> to List<Parcelable>
2159                 List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
2160                 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
2161                 ArrayList<InputMethodSubtype> subtypes = null;
2162                 if (info != null && !info.isEmpty()) {
2163                     final int N = info.size();
2164                     for (int i = 0; i < N; ++i) {
2165                         Object o = info.get(i);
2166                         if (o instanceof InputMethodInfo) {
2167                             if (ret.containsKey(o)) {
2168                                 Log.e(TAG, "IMI list already contains the same InputMethod.");
2169                                 break;
2170                             }
2171                             subtypes = new ArrayList<>();
2172                             ret.put((InputMethodInfo)o, subtypes);
2173                         } else if (subtypes != null && o instanceof InputMethodSubtype) {
2174                             subtypes.add((InputMethodSubtype)o);
2175                         }
2176                     }
2177                 }
2178             } catch (RemoteException e) {
2179                 throw e.rethrowFromSystemServer();
2180             }
2181             return ret;
2182         }
2183     }
2184 
2185     /**
2186      * @return The current height of the input method window.
2187      * @hide
2188      */
getInputMethodWindowVisibleHeight()2189     public int getInputMethodWindowVisibleHeight() {
2190         synchronized (mH) {
2191             try {
2192                 return mService.getInputMethodWindowVisibleHeight();
2193             } catch (RemoteException e) {
2194                 throw e.rethrowFromSystemServer();
2195             }
2196         }
2197     }
2198 
2199     /**
2200      * Tells the system that the IME decided to not show a window and the system no longer needs to
2201      * use the previous IME's inset.
2202      *
2203      * <p>Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()}
2204      * is the only expected caller of this method.  Do not depend on this anywhere else.</p>
2205      *
2206      * <p>TODO: We probably need to reconsider how IME should be handled.</p>
2207      * @hide
2208      * @param token Supplies the identifying token given to an input method when it was started,
2209      * which allows it to perform this operation on itself.
2210      */
clearLastInputMethodWindowForTransition(final IBinder token)2211     public void clearLastInputMethodWindowForTransition(final IBinder token) {
2212         synchronized (mH) {
2213             try {
2214                 mService.clearLastInputMethodWindowForTransition(token);
2215             } catch (RemoteException e) {
2216                 throw e.rethrowFromSystemServer();
2217             }
2218         }
2219     }
2220 
2221     /**
2222      * Force switch to the last used input method and subtype. If the last input method didn't have
2223      * any subtypes, the framework will simply switch to the last input method with no subtype
2224      * specified.
2225      * @param imeToken Supplies the identifying token given to an input method when it was started,
2226      * which allows it to perform this operation on itself.
2227      * @return true if the current input method and subtype was successfully switched to the last
2228      * used input method and subtype.
2229      */
switchToLastInputMethod(IBinder imeToken)2230     public boolean switchToLastInputMethod(IBinder imeToken) {
2231         synchronized (mH) {
2232             try {
2233                 return mService.switchToLastInputMethod(imeToken);
2234             } catch (RemoteException e) {
2235                 throw e.rethrowFromSystemServer();
2236             }
2237         }
2238     }
2239 
2240     /**
2241      * Force switch to the next input method and subtype. If there is no IME enabled except
2242      * current IME and subtype, do nothing.
2243      * @param imeToken Supplies the identifying token given to an input method when it was started,
2244      * which allows it to perform this operation on itself.
2245      * @param onlyCurrentIme if true, the framework will find the next subtype which
2246      * belongs to the current IME
2247      * @return true if the current input method and subtype was successfully switched to the next
2248      * input method and subtype.
2249      */
switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme)2250     public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
2251         synchronized (mH) {
2252             try {
2253                 return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
2254             } catch (RemoteException e) {
2255                 throw e.rethrowFromSystemServer();
2256             }
2257         }
2258     }
2259 
2260     /**
2261      * Returns true if the current IME needs to offer the users ways to switch to a next input
2262      * method (e.g. a globe key.).
2263      * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
2264      * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
2265      * <p> Note that the system determines the most appropriate next input method
2266      * and subtype in order to provide the consistent user experience in switching
2267      * between IMEs and subtypes.
2268      * @param imeToken Supplies the identifying token given to an input method when it was started,
2269      * which allows it to perform this operation on itself.
2270      */
shouldOfferSwitchingToNextInputMethod(IBinder imeToken)2271     public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
2272         synchronized (mH) {
2273             try {
2274                 return mService.shouldOfferSwitchingToNextInputMethod(imeToken);
2275             } catch (RemoteException e) {
2276                 throw e.rethrowFromSystemServer();
2277             }
2278         }
2279     }
2280 
2281     /**
2282      * Set additional input method subtypes. Only a process which shares the same uid with the IME
2283      * can add additional input method subtypes to the IME.
2284      * Please note that a subtype's status is stored in the system.
2285      * For example, enabled subtypes are remembered by the framework even after they are removed
2286      * by using this method. If you re-add the same subtypes again,
2287      * they will just get enabled. If you want to avoid such conflicts, for instance, you may
2288      * want to create a "different" new subtype even with the same locale and mode,
2289      * by changing its extra value. The different subtype won't get affected by the stored past
2290      * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer
2291      * to the current implementation.)
2292      *
2293      * <p>NOTE: If the same subtype exists in both the manifest XML file and additional subtypes
2294      * specified by {@code subtypes}, those multiple instances are automatically merged into one
2295      * instance.</p>
2296      *
2297      * <p>CAVEAT: In API Level 23 and prior, the system may do nothing if an empty
2298      * {@link InputMethodSubtype} is specified in {@code subtypes}, which prevents you from removing
2299      * the last one entry of additional subtypes. If your IME statically defines one or more
2300      * subtypes in the manifest XML file, you may be able to work around this limitation by
2301      * specifying one of those statically defined subtypes in {@code subtypes}.</p>
2302      *
2303      * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
2304      * @param subtypes subtypes will be added as additional subtypes of the current input method.
2305      */
setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes)2306     public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
2307         synchronized (mH) {
2308             try {
2309                 mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
2310             } catch (RemoteException e) {
2311                 throw e.rethrowFromSystemServer();
2312             }
2313         }
2314     }
2315 
getLastInputMethodSubtype()2316     public InputMethodSubtype getLastInputMethodSubtype() {
2317         synchronized (mH) {
2318             try {
2319                 return mService.getLastInputMethodSubtype();
2320             } catch (RemoteException e) {
2321                 throw e.rethrowFromSystemServer();
2322             }
2323         }
2324     }
2325 
2326     /**
2327      * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
2328      * permission to the content.
2329      *
2330      * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, EditorInfo)}
2331      * for details.</p>
2332      *
2333      * @param token Supplies the identifying token given to an input method when it was started,
2334      * which allows it to perform this operation on itself.
2335      * @param inputContentInfo Content to be temporarily exposed from the input method to the
2336      * application.
2337      * This cannot be {@code null}.
2338      * @param editorInfo The editor that receives {@link InputContentInfo}.
2339      * @hide
2340      */
exposeContent(@onNull IBinder token, @NonNull InputContentInfo inputContentInfo, @NonNull EditorInfo editorInfo)2341     public void exposeContent(@NonNull IBinder token, @NonNull InputContentInfo inputContentInfo,
2342             @NonNull EditorInfo editorInfo) {
2343         final IInputContentUriToken uriToken;
2344         final Uri contentUri = inputContentInfo.getContentUri();
2345         try {
2346             uriToken = mService.createInputContentUriToken(token, contentUri,
2347                     editorInfo.packageName);
2348             if (uriToken == null) {
2349                 return;
2350             }
2351         } catch (RemoteException e) {
2352             Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
2353                     + " packageName=" + editorInfo.packageName, e);
2354             return;
2355         }
2356         inputContentInfo.setUriToken(uriToken);
2357         return;
2358     }
2359 
doDump(FileDescriptor fd, PrintWriter fout, String[] args)2360     void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
2361         final Printer p = new PrintWriterPrinter(fout);
2362         p.println("Input method client state for " + this + ":");
2363 
2364         p.println("  mService=" + mService);
2365         p.println("  mMainLooper=" + mMainLooper);
2366         p.println("  mIInputContext=" + mIInputContext);
2367         p.println("  mActive=" + mActive
2368                 + " mHasBeenInactive=" + mHasBeenInactive
2369                 + " mBindSequence=" + mBindSequence
2370                 + " mCurId=" + mCurId);
2371         p.println("  mFullscreenMode=" + mFullscreenMode);
2372         p.println("  mCurMethod=" + mCurMethod);
2373         p.println("  mCurRootView=" + mCurRootView);
2374         p.println("  mServedView=" + mServedView);
2375         p.println("  mNextServedView=" + mNextServedView);
2376         p.println("  mServedConnecting=" + mServedConnecting);
2377         if (mCurrentTextBoxAttribute != null) {
2378             p.println("  mCurrentTextBoxAttribute:");
2379             mCurrentTextBoxAttribute.dump(p, "    ");
2380         } else {
2381             p.println("  mCurrentTextBoxAttribute: null");
2382         }
2383         p.println("  mServedInputConnectionWrapper=" + mServedInputConnectionWrapper);
2384         p.println("  mCompletions=" + Arrays.toString(mCompletions));
2385         p.println("  mCursorRect=" + mCursorRect);
2386         p.println("  mCursorSelStart=" + mCursorSelStart
2387                 + " mCursorSelEnd=" + mCursorSelEnd
2388                 + " mCursorCandStart=" + mCursorCandStart
2389                 + " mCursorCandEnd=" + mCursorCandEnd);
2390         p.println("  mNextUserActionNotificationSequenceNumber="
2391                 + mNextUserActionNotificationSequenceNumber
2392                 + " mLastSentUserActionNotificationSequenceNumber="
2393                 + mLastSentUserActionNotificationSequenceNumber);
2394     }
2395 
2396     /**
2397      * Callback that is invoked when an input event that was dispatched to
2398      * the IME has been finished.
2399      * @hide
2400      */
2401     public interface FinishedInputEventCallback {
onFinishedInputEvent(Object token, boolean handled)2402         public void onFinishedInputEvent(Object token, boolean handled);
2403     }
2404 
2405     private final class ImeInputEventSender extends InputEventSender {
ImeInputEventSender(InputChannel inputChannel, Looper looper)2406         public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
2407             super(inputChannel, looper);
2408         }
2409 
2410         @Override
onInputEventFinished(int seq, boolean handled)2411         public void onInputEventFinished(int seq, boolean handled) {
2412             finishedInputEvent(seq, handled, false);
2413         }
2414     }
2415 
2416     private final class PendingEvent implements Runnable {
2417         public InputEvent mEvent;
2418         public Object mToken;
2419         public String mInputMethodId;
2420         public FinishedInputEventCallback mCallback;
2421         public Handler mHandler;
2422         public boolean mHandled;
2423 
recycle()2424         public void recycle() {
2425             mEvent = null;
2426             mToken = null;
2427             mInputMethodId = null;
2428             mCallback = null;
2429             mHandler = null;
2430             mHandled = false;
2431         }
2432 
2433         @Override
run()2434         public void run() {
2435             mCallback.onFinishedInputEvent(mToken, mHandled);
2436 
2437             synchronized (mH) {
2438                 recyclePendingEventLocked(this);
2439             }
2440         }
2441     }
2442 
dumpViewInfo(@ullable final View view)2443     private static String dumpViewInfo(@Nullable final View view) {
2444         if (view == null) {
2445             return "null";
2446         }
2447         final StringBuilder sb = new StringBuilder();
2448         sb.append(view);
2449         sb.append(",focus=" + view.hasFocus());
2450         sb.append(",windowFocus=" + view.hasWindowFocus());
2451         sb.append(",window=" + view.getWindowToken());
2452         sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
2453         return sb.toString();
2454     }
2455 }
2456