• 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 com.android.internal.os.SomeArgs;
20 import com.android.internal.view.IInputConnectionWrapper;
21 import com.android.internal.view.IInputContext;
22 import com.android.internal.view.IInputMethodClient;
23 import com.android.internal.view.IInputMethodManager;
24 import com.android.internal.view.IInputMethodSession;
25 import com.android.internal.view.InputBindResult;
26 
27 import android.content.Context;
28 import android.graphics.Rect;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.RemoteException;
35 import android.os.ResultReceiver;
36 import android.os.ServiceManager;
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.view.InputChannel;
45 import android.view.InputEvent;
46 import android.view.InputEventSender;
47 import android.view.KeyEvent;
48 import android.view.View;
49 import android.view.ViewRootImpl;
50 import android.util.SparseArray;
51 
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
54 import java.util.ArrayList;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.concurrent.CountDownLatch;
59 import java.util.concurrent.TimeUnit;
60 
61 /**
62  * Central system API to the overall input method framework (IMF) architecture,
63  * which arbitrates interaction between applications and the current input method.
64  * You can retrieve an instance of this interface with
65  * {@link Context#getSystemService(String) Context.getSystemService()}.
66  *
67  * <p>Topics covered here:
68  * <ol>
69  * <li><a href="#ArchitectureOverview">Architecture Overview</a>
70  * <li><a href="#Applications">Applications</a>
71  * <li><a href="#InputMethods">Input Methods</a>
72  * <li><a href="#Security">Security</a>
73  * </ol>
74  *
75  * <a name="ArchitectureOverview"></a>
76  * <h3>Architecture Overview</h3>
77  *
78  * <p>There are three primary parties involved in the input method
79  * framework (IMF) architecture:</p>
80  *
81  * <ul>
82  * <li> The <strong>input method manager</strong> as expressed by this class
83  * is the central point of the system that manages interaction between all
84  * other parts.  It is expressed as the client-side API here which exists
85  * in each application context and communicates with a global system service
86  * that manages the interaction across all processes.
87  * <li> An <strong>input method (IME)</strong> implements a particular
88  * interaction model allowing the user to generate text.  The system binds
89  * to the current input method that is use, causing it to be created and run,
90  * and tells it when to hide and show its UI.  Only one IME is running at a time.
91  * <li> Multiple <strong>client applications</strong> arbitrate with the input
92  * method manager for input focus and control over the state of the IME.  Only
93  * one such client is ever active (working with the IME) at a time.
94  * </ul>
95  *
96  *
97  * <a name="Applications"></a>
98  * <h3>Applications</h3>
99  *
100  * <p>In most cases, applications that are using the standard
101  * {@link android.widget.TextView} or its subclasses will have little they need
102  * to do to work well with soft input methods.  The main things you need to
103  * be aware of are:</p>
104  *
105  * <ul>
106  * <li> Properly set the {@link android.R.attr#inputType} in your editable
107  * text views, so that the input method will have enough context to help the
108  * user in entering text into them.
109  * <li> Deal well with losing screen space when the input method is
110  * displayed.  Ideally an application should handle its window being resized
111  * smaller, but it can rely on the system performing panning of the window
112  * if needed.  You should set the {@link android.R.attr#windowSoftInputMode}
113  * attribute on your activity or the corresponding values on windows you
114  * create to help the system determine whether to pan or resize (it will
115  * try to determine this automatically but may get it wrong).
116  * <li> You can also control the preferred soft input state (open, closed, etc)
117  * for your window using the same {@link android.R.attr#windowSoftInputMode}
118  * attribute.
119  * </ul>
120  *
121  * <p>More finer-grained control is available through the APIs here to directly
122  * interact with the IMF and its IME -- either showing or hiding the input
123  * area, letting the user pick an input method, etc.</p>
124  *
125  * <p>For the rare people amongst us writing their own text editors, you
126  * will need to implement {@link android.view.View#onCreateInputConnection}
127  * to return a new instance of your own {@link InputConnection} interface
128  * allowing the IME to interact with your editor.</p>
129  *
130  *
131  * <a name="InputMethods"></a>
132  * <h3>Input Methods</h3>
133  *
134  * <p>An input method (IME) is implemented
135  * as a {@link android.app.Service}, typically deriving from
136  * {@link android.inputmethodservice.InputMethodService}.  It must provide
137  * the core {@link InputMethod} interface, though this is normally handled by
138  * {@link android.inputmethodservice.InputMethodService} and implementors will
139  * only need to deal with the higher-level API there.</p>
140  *
141  * See the {@link android.inputmethodservice.InputMethodService} class for
142  * more information on implementing IMEs.
143  *
144  *
145  * <a name="Security"></a>
146  * <h3>Security</h3>
147  *
148  * <p>There are a lot of security issues associated with input methods,
149  * since they essentially have freedom to completely drive the UI and monitor
150  * everything the user enters.  The Android input method framework also allows
151  * arbitrary third party IMEs, so care must be taken to restrict their
152  * selection and interactions.</p>
153  *
154  * <p>Here are some key points about the security architecture behind the
155  * IMF:</p>
156  *
157  * <ul>
158  * <li> <p>Only the system is allowed to directly access an IME's
159  * {@link InputMethod} interface, via the
160  * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission.  This is
161  * enforced in the system by not binding to an input method service that does
162  * not require this permission, so the system can guarantee no other untrusted
163  * clients are accessing the current input method outside of its control.</p>
164  *
165  * <li> <p>There may be many client processes of the IMF, but only one may
166  * be active at a time.  The inactive clients can not interact with key
167  * parts of the IMF through the mechanisms described below.</p>
168  *
169  * <li> <p>Clients of an input method are only given access to its
170  * {@link InputMethodSession} interface.  One instance of this interface is
171  * created for each client, and only calls from the session associated with
172  * the active client will be processed by the current IME.  This is enforced
173  * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
174  * IMEs, but must be explicitly handled by an IME that is customizing the
175  * raw {@link InputMethodSession} implementation.</p>
176  *
177  * <li> <p>Only the active client's {@link InputConnection} will accept
178  * operations.  The IMF tells each client process whether it is active, and
179  * the framework enforces that in inactive processes calls on to the current
180  * InputConnection will be ignored.  This ensures that the current IME can
181  * only deliver events and text edits to the UI that the user sees as
182  * being in focus.</p>
183  *
184  * <li> <p>An IME can never interact with an {@link InputConnection} while
185  * the screen is off.  This is enforced by making all clients inactive while
186  * the screen is off, and prevents bad IMEs from driving the UI when the user
187  * can not be aware of its behavior.</p>
188  *
189  * <li> <p>A client application can ask that the system let the user pick a
190  * new IME, but can not programmatically switch to one itself.  This avoids
191  * malicious applications from switching the user to their own IME, which
192  * remains running when the user navigates away to another application.  An
193  * IME, on the other hand, <em>is</em> allowed to programmatically switch
194  * the system to another IME, since it already has full control of user
195  * input.</p>
196  *
197  * <li> <p>The user must explicitly enable a new IME in settings before
198  * they can switch to it, to confirm with the system that they know about it
199  * and want to make it available for use.</p>
200  * </ul>
201  */
202 public final class InputMethodManager {
203     static final boolean DEBUG = false;
204     static final String TAG = "InputMethodManager";
205 
206     static final String PENDING_EVENT_COUNTER = "aq:imm";
207 
208     static InputMethodManager sInstance;
209 
210     /**
211      * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
212      * the window has input focus.
213      */
214     public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0;
215 
216     /**
217      * @hide Flag for IInputMethodManager.windowGainedFocus: the focus
218      * is a text editor.
219      */
220     public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1;
221 
222     /**
223      * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first
224      * time the window has gotten focus.
225      */
226     public static final int CONTROL_WINDOW_FIRST = 1<<2;
227 
228     /**
229      * @hide Flag for IInputMethodManager.startInput: this is the first
230      * time the window has gotten focus.
231      */
232     public static final int CONTROL_START_INITIAL = 1<<8;
233 
234     /**
235      * Timeout in milliseconds for delivering a key to an IME.
236      */
237     static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
238 
239     /** @hide */
240     public static final int DISPATCH_IN_PROGRESS = -1;
241 
242     /** @hide */
243     public static final int DISPATCH_NOT_HANDLED = 0;
244 
245     /** @hide */
246     public static final int DISPATCH_HANDLED = 1;
247 
248     final IInputMethodManager mService;
249     final Looper mMainLooper;
250 
251     // For scheduling work on the main thread.  This also serves as our
252     // global lock.
253     final H mH;
254 
255     // Our generic input connection if the current target does not have its own.
256     final IInputContext mIInputContext;
257 
258     /**
259      * True if this input method client is active, initially false.
260      */
261     boolean mActive = false;
262 
263     /**
264      * Set whenever this client becomes inactive, to know we need to reset
265      * state with the IME the next time we receive focus.
266      */
267     boolean mHasBeenInactive = true;
268 
269     /**
270      * As reported by IME through InputConnection.
271      */
272     boolean mFullscreenMode;
273 
274     // -----------------------------------------------------------
275 
276     /**
277      * This is the root view of the overall window that currently has input
278      * method focus.
279      */
280     View mCurRootView;
281     /**
282      * This is the view that should currently be served by an input method,
283      * regardless of the state of setting that up.
284      */
285     View mServedView;
286     /**
287      * This is then next view that will be served by the input method, when
288      * we get around to updating things.
289      */
290     View mNextServedView;
291     /**
292      * This is set when we are in the process of connecting, to determine
293      * when we have actually finished.
294      */
295     boolean mServedConnecting;
296     /**
297      * This is non-null when we have connected the served view; it holds
298      * the attributes that were last retrieved from the served view and given
299      * to the input connection.
300      */
301     EditorInfo mCurrentTextBoxAttribute;
302     /**
303      * The InputConnection that was last retrieved from the served view.
304      */
305     InputConnection mServedInputConnection;
306     ControlledInputConnectionWrapper mServedInputConnectionWrapper;
307     /**
308      * The completions that were last provided by the served view.
309      */
310     CompletionInfo[] mCompletions;
311 
312     // Cursor position on the screen.
313     Rect mTmpCursorRect = new Rect();
314     Rect mCursorRect = new Rect();
315     int mCursorSelStart;
316     int mCursorSelEnd;
317     int mCursorCandStart;
318     int mCursorCandEnd;
319 
320     // -----------------------------------------------------------
321 
322     /**
323      * Sequence number of this binding, as returned by the server.
324      */
325     int mBindSequence = -1;
326     /**
327      * ID of the method we are bound to.
328      */
329     String mCurId;
330     /**
331      * The actual instance of the method to make calls on it.
332      */
333     IInputMethodSession mCurMethod;
334     InputChannel mCurChannel;
335     ImeInputEventSender mCurSender;
336 
337     final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
338     final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
339 
340     // -----------------------------------------------------------
341 
342     static final int MSG_DUMP = 1;
343     static final int MSG_BIND = 2;
344     static final int MSG_UNBIND = 3;
345     static final int MSG_SET_ACTIVE = 4;
346     static final int MSG_SEND_INPUT_EVENT = 5;
347     static final int MSG_TIMEOUT_INPUT_EVENT = 6;
348     static final int MSG_FLUSH_INPUT_EVENT = 7;
349 
350     class H extends Handler {
H(Looper looper)351         H(Looper looper) {
352             super(looper, null, true);
353         }
354 
355         @Override
handleMessage(Message msg)356         public void handleMessage(Message msg) {
357             switch (msg.what) {
358                 case MSG_DUMP: {
359                     SomeArgs args = (SomeArgs)msg.obj;
360                     try {
361                         doDump((FileDescriptor)args.arg1,
362                                 (PrintWriter)args.arg2, (String[])args.arg3);
363                     } catch (RuntimeException e) {
364                         ((PrintWriter)args.arg2).println("Exception: " + e);
365                     }
366                     synchronized (args.arg4) {
367                         ((CountDownLatch)args.arg4).countDown();
368                     }
369                     args.recycle();
370                     return;
371                 }
372                 case MSG_BIND: {
373                     final InputBindResult res = (InputBindResult)msg.obj;
374                     if (DEBUG) {
375                         Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
376                     }
377                     synchronized (mH) {
378                         if (mBindSequence < 0 || mBindSequence != res.sequence) {
379                             Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
380                                     + ", given seq=" + res.sequence);
381                             if (res.channel != null && res.channel != mCurChannel) {
382                                 res.channel.dispose();
383                             }
384                             return;
385                         }
386 
387                         setInputChannelLocked(res.channel);
388                         mCurMethod = res.method;
389                         mCurId = res.id;
390                         mBindSequence = res.sequence;
391                     }
392                     startInputInner(null, 0, 0, 0);
393                     return;
394                 }
395                 case MSG_UNBIND: {
396                     final int sequence = msg.arg1;
397                     if (DEBUG) {
398                         Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence);
399                     }
400                     boolean startInput = false;
401                     synchronized (mH) {
402                         if (mBindSequence == sequence) {
403                             if (false) {
404                                 // XXX the server has already unbound!
405                                 if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
406                                     try {
407                                         mCurMethod.finishInput();
408                                     } catch (RemoteException e) {
409                                         Log.w(TAG, "IME died: " + mCurId, e);
410                                     }
411                                 }
412                             }
413                             clearBindingLocked();
414 
415                             // If we were actively using the last input method, then
416                             // we would like to re-connect to the next input method.
417                             if (mServedView != null && mServedView.isFocused()) {
418                                 mServedConnecting = true;
419                             }
420                             if (mActive) {
421                                 startInput = true;
422                             }
423                         }
424                     }
425                     if (startInput) {
426                         startInputInner(null, 0, 0, 0);
427                     }
428                     return;
429                 }
430                 case MSG_SET_ACTIVE: {
431                     final boolean active = msg.arg1 != 0;
432                     if (DEBUG) {
433                         Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive);
434                     }
435                     synchronized (mH) {
436                         mActive = active;
437                         mFullscreenMode = false;
438                         if (!active) {
439                             // Some other client has starting using the IME, so note
440                             // that this happened and make sure our own editor's
441                             // state is reset.
442                             mHasBeenInactive = true;
443                             try {
444                                 // Note that finishComposingText() is allowed to run
445                                 // even when we are not active.
446                                 mIInputContext.finishComposingText();
447                             } catch (RemoteException e) {
448                             }
449                             // Check focus again in case that "onWindowFocus" is called before
450                             // handling this message.
451                             if (mServedView != null && mServedView.hasWindowFocus()) {
452                                 // "finishComposingText" has been already called above. So we
453                                 // should not call mServedInputConnection.finishComposingText here.
454                                 // Also, please note that this handler thread could be different
455                                 // from a thread that created mServedView. That could happen
456                                 // the current activity is running in the system process.
457                                 // In that case, we really should not call
458                                 // mServedInputConnection.finishComposingText.
459                                 if (checkFocusNoStartInput(mHasBeenInactive, false)) {
460                                     startInputInner(null, 0, 0, 0);
461                                 }
462                             }
463                         }
464                     }
465                     return;
466                 }
467                 case MSG_SEND_INPUT_EVENT: {
468                     sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
469                     return;
470                 }
471                 case MSG_TIMEOUT_INPUT_EVENT: {
472                     finishedInputEvent(msg.arg1, false, true);
473                     return;
474                 }
475                 case MSG_FLUSH_INPUT_EVENT: {
476                     finishedInputEvent(msg.arg1, false, false);
477                     return;
478                 }
479             }
480         }
481     }
482 
483     private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
484         private final InputMethodManager mParentInputMethodManager;
485         private boolean mActive;
486 
ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn, final InputMethodManager inputMethodManager)487         public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
488                 final InputMethodManager inputMethodManager) {
489             super(mainLooper, conn);
490             mParentInputMethodManager = inputMethodManager;
491             mActive = true;
492         }
493 
494         @Override
isActive()495         public boolean isActive() {
496             return mParentInputMethodManager.mActive && mActive;
497         }
498 
deactivate()499         void deactivate() {
500             mActive = false;
501         }
502     }
503 
504     final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
505         @Override
506         protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
507             // No need to check for dump permission, since we only give this
508             // interface to the system.
509             CountDownLatch latch = new CountDownLatch(1);
510             SomeArgs sargs = SomeArgs.obtain();
511             sargs.arg1 = fd;
512             sargs.arg2 = fout;
513             sargs.arg3 = args;
514             sargs.arg4 = latch;
515             mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
516             try {
517                 if (!latch.await(5, TimeUnit.SECONDS)) {
518                     fout.println("Timeout waiting for dump");
519                 }
520             } catch (InterruptedException e) {
521                 fout.println("Interrupted waiting for dump");
522             }
523         }
524 
525         @Override
526         public void setUsingInputMethod(boolean state) {
527         }
528 
529         @Override
530         public void onBindMethod(InputBindResult res) {
531             mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
532         }
533 
534         @Override
535         public void onUnbindMethod(int sequence) {
536             mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
537         }
538 
539         @Override
540         public void setActive(boolean active) {
541             mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
542         }
543     };
544 
545     final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
546 
InputMethodManager(IInputMethodManager service, Looper looper)547     InputMethodManager(IInputMethodManager service, Looper looper) {
548         mService = service;
549         mMainLooper = looper;
550         mH = new H(looper);
551         mIInputContext = new ControlledInputConnectionWrapper(looper,
552                 mDummyInputConnection, this);
553     }
554 
555     /**
556      * Retrieve the global InputMethodManager instance, creating it if it
557      * doesn't already exist.
558      * @hide
559      */
getInstance()560     public static InputMethodManager getInstance() {
561         synchronized (InputMethodManager.class) {
562             if (sInstance == null) {
563                 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
564                 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
565                 sInstance = new InputMethodManager(service, Looper.getMainLooper());
566             }
567             return sInstance;
568         }
569     }
570 
571     /**
572      * Private optimization: retrieve the global InputMethodManager instance,
573      * if it exists.
574      * @hide
575      */
peekInstance()576     public static InputMethodManager peekInstance() {
577         return sInstance;
578     }
579 
580     /** @hide */
getClient()581     public IInputMethodClient getClient() {
582         return mClient;
583     }
584 
585     /** @hide */
getInputContext()586     public IInputContext getInputContext() {
587         return mIInputContext;
588     }
589 
getInputMethodList()590     public List<InputMethodInfo> getInputMethodList() {
591         try {
592             return mService.getInputMethodList();
593         } catch (RemoteException e) {
594             throw new RuntimeException(e);
595         }
596     }
597 
getEnabledInputMethodList()598     public List<InputMethodInfo> getEnabledInputMethodList() {
599         try {
600             return mService.getEnabledInputMethodList();
601         } catch (RemoteException e) {
602             throw new RuntimeException(e);
603         }
604     }
605 
606     /**
607      * Returns a list of enabled input method subtypes for the specified input method info.
608      * @param imi An input method info whose subtypes list will be returned.
609      * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
610      * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
611      * will implicitly enable subtypes according to the current system language.
612      */
getEnabledInputMethodSubtypeList(InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes)613     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
614             boolean allowsImplicitlySelectedSubtypes) {
615         try {
616             return mService.getEnabledInputMethodSubtypeList(imi, allowsImplicitlySelectedSubtypes);
617         } catch (RemoteException e) {
618             throw new RuntimeException(e);
619         }
620     }
621 
showStatusIcon(IBinder imeToken, String packageName, int iconId)622     public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
623         try {
624             mService.updateStatusIcon(imeToken, packageName, iconId);
625         } catch (RemoteException e) {
626             throw new RuntimeException(e);
627         }
628     }
629 
hideStatusIcon(IBinder imeToken)630     public void hideStatusIcon(IBinder imeToken) {
631         try {
632             mService.updateStatusIcon(imeToken, null, 0);
633         } catch (RemoteException e) {
634             throw new RuntimeException(e);
635         }
636     }
637 
638     /** @hide */
setImeWindowStatus(IBinder imeToken, int vis, int backDisposition)639     public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
640         try {
641             mService.setImeWindowStatus(imeToken, vis, backDisposition);
642         } catch (RemoteException e) {
643             throw new RuntimeException(e);
644         }
645     }
646 
647     /** @hide */
setFullscreenMode(boolean fullScreen)648     public void setFullscreenMode(boolean fullScreen) {
649         mFullscreenMode = fullScreen;
650     }
651 
652     /** @hide */
registerSuggestionSpansForNotification(SuggestionSpan[] spans)653     public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
654         try {
655             mService.registerSuggestionSpansForNotification(spans);
656         } catch (RemoteException e) {
657             throw new RuntimeException(e);
658         }
659     }
660 
661     /** @hide */
notifySuggestionPicked(SuggestionSpan span, String originalString, int index)662     public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
663         try {
664             mService.notifySuggestionPicked(span, originalString, index);
665         } catch (RemoteException e) {
666             throw new RuntimeException(e);
667         }
668     }
669 
670     /**
671      * Allows you to discover whether the attached input method is running
672      * in fullscreen mode.  Return true if it is fullscreen, entirely covering
673      * your UI, else returns false.
674      */
isFullscreenMode()675     public boolean isFullscreenMode() {
676         return mFullscreenMode;
677     }
678 
679     /**
680      * Return true if the given view is the currently active view for the
681      * input method.
682      */
isActive(View view)683     public boolean isActive(View view) {
684         checkFocus();
685         synchronized (mH) {
686             return (mServedView == view
687                     || (mServedView != null
688                             && mServedView.checkInputConnectionProxy(view)))
689                     && mCurrentTextBoxAttribute != null;
690         }
691     }
692 
693     /**
694      * Return true if any view is currently active in the input method.
695      */
isActive()696     public boolean isActive() {
697         checkFocus();
698         synchronized (mH) {
699             return mServedView != null && mCurrentTextBoxAttribute != null;
700         }
701     }
702 
703     /**
704      * Return true if the currently served view is accepting full text edits.
705      * If false, it has no input connection, so can only handle raw key events.
706      */
isAcceptingText()707     public boolean isAcceptingText() {
708         checkFocus();
709         return mServedInputConnection != null;
710     }
711 
712     /**
713      * Reset all of the state associated with being bound to an input method.
714      */
clearBindingLocked()715     void clearBindingLocked() {
716         clearConnectionLocked();
717         setInputChannelLocked(null);
718         mBindSequence = -1;
719         mCurId = null;
720         mCurMethod = null;
721     }
722 
setInputChannelLocked(InputChannel channel)723     void setInputChannelLocked(InputChannel channel) {
724         if (mCurChannel != channel) {
725             if (mCurSender != null) {
726                 flushPendingEventsLocked();
727                 mCurSender.dispose();
728                 mCurSender = null;
729             }
730             if (mCurChannel != null) {
731                 mCurChannel.dispose();
732             }
733             mCurChannel = channel;
734         }
735     }
736 
737     /**
738      * Reset all of the state associated with a served view being connected
739      * to an input method
740      */
clearConnectionLocked()741     void clearConnectionLocked() {
742         mCurrentTextBoxAttribute = null;
743         mServedInputConnection = null;
744         if (mServedInputConnectionWrapper != null) {
745             mServedInputConnectionWrapper.deactivate();
746             mServedInputConnectionWrapper = null;
747         }
748     }
749 
750     /**
751      * Disconnect any existing input connection, clearing the served view.
752      */
finishInputLocked()753     void finishInputLocked() {
754         mCurRootView = null;
755         mNextServedView = null;
756         if (mServedView != null) {
757             if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
758 
759             if (mCurrentTextBoxAttribute != null) {
760                 try {
761                     mService.finishInput(mClient);
762                 } catch (RemoteException e) {
763                 }
764             }
765 
766             notifyInputConnectionFinished();
767 
768             mServedView = null;
769             mCompletions = null;
770             mServedConnecting = false;
771             clearConnectionLocked();
772         }
773     }
774 
775     /**
776      * Notifies the served view that the current InputConnection will no longer be used.
777      */
notifyInputConnectionFinished()778     private void notifyInputConnectionFinished() {
779         if (mServedView != null && mServedInputConnection != null) {
780             // We need to tell the previously served view that it is no
781             // longer the input target, so it can reset its state.  Schedule
782             // this call on its window's Handler so it will be on the correct
783             // thread and outside of our lock.
784             ViewRootImpl viewRootImpl = mServedView.getViewRootImpl();
785             if (viewRootImpl != null) {
786                 // This will result in a call to reportFinishInputConnection() below.
787                 viewRootImpl.dispatchFinishInputConnection(mServedInputConnection);
788             }
789         }
790     }
791 
792     /**
793      * Called from the FINISH_INPUT_CONNECTION message above.
794      * @hide
795      */
reportFinishInputConnection(InputConnection ic)796     public void reportFinishInputConnection(InputConnection ic) {
797         if (mServedInputConnection != ic) {
798             ic.finishComposingText();
799             // To avoid modifying the public InputConnection interface
800             if (ic instanceof BaseInputConnection) {
801                 ((BaseInputConnection) ic).reportFinish();
802             }
803         }
804     }
805 
displayCompletions(View view, CompletionInfo[] completions)806     public void displayCompletions(View view, CompletionInfo[] completions) {
807         checkFocus();
808         synchronized (mH) {
809             if (mServedView != view && (mServedView == null
810                             || !mServedView.checkInputConnectionProxy(view))) {
811                 return;
812             }
813 
814             mCompletions = completions;
815             if (mCurMethod != null) {
816                 try {
817                     mCurMethod.displayCompletions(mCompletions);
818                 } catch (RemoteException e) {
819                 }
820             }
821         }
822     }
823 
updateExtractedText(View view, int token, ExtractedText text)824     public void updateExtractedText(View view, int token, ExtractedText text) {
825         checkFocus();
826         synchronized (mH) {
827             if (mServedView != view && (mServedView == null
828                     || !mServedView.checkInputConnectionProxy(view))) {
829                 return;
830             }
831 
832             if (mCurMethod != null) {
833                 try {
834                     mCurMethod.updateExtractedText(token, text);
835                 } catch (RemoteException e) {
836                 }
837             }
838         }
839     }
840 
841     /**
842      * Flag for {@link #showSoftInput} to indicate that this is an implicit
843      * request to show the input window, not as the result of a direct request
844      * by the user.  The window may not be shown in this case.
845      */
846     public static final int SHOW_IMPLICIT = 0x0001;
847 
848     /**
849      * Flag for {@link #showSoftInput} to indicate that the user has forced
850      * the input method open (such as by long-pressing menu) so it should
851      * not be closed until they explicitly do so.
852      */
853     public static final int SHOW_FORCED = 0x0002;
854 
855     /**
856      * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
857      * a result receiver: explicitly request that the current input method's
858      * soft input area be shown to the user, if needed.
859      *
860      * @param view The currently focused view, which would like to receive
861      * soft keyboard input.
862      * @param flags Provides additional operating flags.  Currently may be
863      * 0 or have the {@link #SHOW_IMPLICIT} bit set.
864      */
showSoftInput(View view, int flags)865     public boolean showSoftInput(View view, int flags) {
866         return showSoftInput(view, flags, null);
867     }
868 
869     /**
870      * Flag for the {@link ResultReceiver} result code from
871      * {@link #showSoftInput(View, int, ResultReceiver)} and
872      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
873      * state of the soft input window was unchanged and remains shown.
874      */
875     public static final int RESULT_UNCHANGED_SHOWN = 0;
876 
877     /**
878      * Flag for the {@link ResultReceiver} result code from
879      * {@link #showSoftInput(View, int, ResultReceiver)} and
880      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
881      * state of the soft input window was unchanged and remains hidden.
882      */
883     public static final int RESULT_UNCHANGED_HIDDEN = 1;
884 
885     /**
886      * Flag for the {@link ResultReceiver} result code from
887      * {@link #showSoftInput(View, int, ResultReceiver)} and
888      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
889      * state of the soft input window changed from hidden to shown.
890      */
891     public static final int RESULT_SHOWN = 2;
892 
893     /**
894      * Flag for the {@link ResultReceiver} result code from
895      * {@link #showSoftInput(View, int, ResultReceiver)} and
896      * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
897      * state of the soft input window changed from shown to hidden.
898      */
899     public static final int RESULT_HIDDEN = 3;
900 
901     /**
902      * Explicitly request that the current input method's soft input area be
903      * shown to the user, if needed.  Call this if the user interacts with
904      * your view in such a way that they have expressed they would like to
905      * start performing input into it.
906      *
907      * @param view The currently focused view, which would like to receive
908      * soft keyboard input.
909      * @param flags Provides additional operating flags.  Currently may be
910      * 0 or have the {@link #SHOW_IMPLICIT} bit set.
911      * @param resultReceiver If non-null, this will be called by the IME when
912      * it has processed your request to tell you what it has done.  The result
913      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
914      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
915      * {@link #RESULT_HIDDEN}.
916      */
showSoftInput(View view, int flags, ResultReceiver resultReceiver)917     public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
918         checkFocus();
919         synchronized (mH) {
920             if (mServedView != view && (mServedView == null
921                     || !mServedView.checkInputConnectionProxy(view))) {
922                 return false;
923             }
924 
925             try {
926                 return mService.showSoftInput(mClient, flags, resultReceiver);
927             } catch (RemoteException e) {
928             }
929 
930             return false;
931         }
932     }
933 
934     /** @hide */
showSoftInputUnchecked(int flags, ResultReceiver resultReceiver)935     public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
936         try {
937             mService.showSoftInput(mClient, flags, resultReceiver);
938         } catch (RemoteException e) {
939         }
940     }
941 
942     /**
943      * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
944      * input window should only be hidden if it was not explicitly shown
945      * by the user.
946      */
947     public static final int HIDE_IMPLICIT_ONLY = 0x0001;
948 
949     /**
950      * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
951      * input window should normally be hidden, unless it was originally
952      * shown with {@link #SHOW_FORCED}.
953      */
954     public static final int HIDE_NOT_ALWAYS = 0x0002;
955 
956     /**
957      * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}
958      * without a result: request to hide the soft input window from the
959      * context of the window that is currently accepting input.
960      *
961      * @param windowToken The token of the window that is making the request,
962      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
963      * @param flags Provides additional operating flags.  Currently may be
964      * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
965      */
hideSoftInputFromWindow(IBinder windowToken, int flags)966     public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
967         return hideSoftInputFromWindow(windowToken, flags, null);
968     }
969 
970     /**
971      * Request to hide the soft input window from the context of the window
972      * that is currently accepting input.  This should be called as a result
973      * of the user doing some actually than fairly explicitly requests to
974      * have the input window hidden.
975      *
976      * @param windowToken The token of the window that is making the request,
977      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
978      * @param flags Provides additional operating flags.  Currently may be
979      * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
980      * @param resultReceiver If non-null, this will be called by the IME when
981      * it has processed your request to tell you what it has done.  The result
982      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
983      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
984      * {@link #RESULT_HIDDEN}.
985      */
hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver)986     public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
987             ResultReceiver resultReceiver) {
988         checkFocus();
989         synchronized (mH) {
990             if (mServedView == null || mServedView.getWindowToken() != windowToken) {
991                 return false;
992             }
993 
994             try {
995                 return mService.hideSoftInput(mClient, flags, resultReceiver);
996             } catch (RemoteException e) {
997             }
998             return false;
999         }
1000     }
1001 
1002 
1003     /**
1004      * This method toggles the input method window display.
1005      * If the input window is already displayed, it gets hidden.
1006      * If not the input window will be displayed.
1007      * @param windowToken The token of the window that is making the request,
1008      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
1009      * @param showFlags Provides additional operating flags.  May be
1010      * 0 or have the {@link #SHOW_IMPLICIT},
1011      * {@link #SHOW_FORCED} bit set.
1012      * @param hideFlags Provides additional operating flags.  May be
1013      * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1014      * {@link #HIDE_NOT_ALWAYS} bit set.
1015      **/
toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags)1016     public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
1017         synchronized (mH) {
1018             if (mServedView == null || mServedView.getWindowToken() != windowToken) {
1019                 return;
1020             }
1021             if (mCurMethod != null) {
1022                 try {
1023                     mCurMethod.toggleSoftInput(showFlags, hideFlags);
1024                 } catch (RemoteException e) {
1025                 }
1026             }
1027         }
1028     }
1029 
1030     /*
1031      * This method toggles the input method window display.
1032      * If the input window is already displayed, it gets hidden.
1033      * If not the input window will be displayed.
1034      * @param showFlags Provides additional operating flags.  May be
1035      * 0 or have the {@link #SHOW_IMPLICIT},
1036      * {@link #SHOW_FORCED} bit set.
1037      * @param hideFlags Provides additional operating flags.  May be
1038      * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1039      * {@link #HIDE_NOT_ALWAYS} bit set.
1040      * @hide
1041      */
toggleSoftInput(int showFlags, int hideFlags)1042     public void toggleSoftInput(int showFlags, int hideFlags) {
1043         if (mCurMethod != null) {
1044             try {
1045                 mCurMethod.toggleSoftInput(showFlags, hideFlags);
1046             } catch (RemoteException e) {
1047             }
1048         }
1049     }
1050 
1051     /**
1052      * If the input method is currently connected to the given view,
1053      * restart it with its new contents.  You should call this when the text
1054      * within your view changes outside of the normal input method or key
1055      * input flow, such as when an application calls TextView.setText().
1056      *
1057      * @param view The view whose text has changed.
1058      */
restartInput(View view)1059     public void restartInput(View view) {
1060         checkFocus();
1061         synchronized (mH) {
1062             if (mServedView != view && (mServedView == null
1063                     || !mServedView.checkInputConnectionProxy(view))) {
1064                 return;
1065             }
1066 
1067             mServedConnecting = true;
1068         }
1069 
1070         startInputInner(null, 0, 0, 0);
1071     }
1072 
startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode, int windowFlags)1073     boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
1074             int windowFlags) {
1075         final View view;
1076         synchronized (mH) {
1077             view = mServedView;
1078 
1079             // Make sure we have a window token for the served view.
1080             if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
1081             if (view == null) {
1082                 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
1083                 return false;
1084             }
1085         }
1086 
1087         // Now we need to get an input connection from the served view.
1088         // This is complicated in a couple ways: we can't be holding our lock
1089         // when calling out to the view, and we need to make sure we call into
1090         // the view on the same thread that is driving its view hierarchy.
1091         Handler vh = view.getHandler();
1092         if (vh == null) {
1093             // If the view doesn't have a handler, something has changed out
1094             // from under us, so just close the current input.
1095             // If we don't close the current input, the current input method can remain on the
1096             // screen without a connection.
1097             if (DEBUG) Log.v(TAG, "ABORT input: no handler for view! Close current input.");
1098             closeCurrentInput();
1099             return false;
1100         }
1101         if (vh.getLooper() != Looper.myLooper()) {
1102             // The view is running on a different thread than our own, so
1103             // we need to reschedule our work for over there.
1104             if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
1105             vh.post(new Runnable() {
1106                 @Override
1107                 public void run() {
1108                     startInputInner(null, 0, 0, 0);
1109                 }
1110             });
1111             return false;
1112         }
1113 
1114         // Okay we are now ready to call into the served view and have it
1115         // do its stuff.
1116         // Life is good: let's hook everything up!
1117         EditorInfo tba = new EditorInfo();
1118         tba.packageName = view.getContext().getPackageName();
1119         tba.fieldId = view.getId();
1120         InputConnection ic = view.onCreateInputConnection(tba);
1121         if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
1122 
1123         synchronized (mH) {
1124             // Now that we are locked again, validate that our state hasn't
1125             // changed.
1126             if (mServedView != view || !mServedConnecting) {
1127                 // Something else happened, so abort.
1128                 if (DEBUG) Log.v(TAG,
1129                         "Starting input: finished by someone else (view="
1130                         + mServedView + " conn=" + mServedConnecting + ")");
1131                 return false;
1132             }
1133 
1134             // If we already have a text box, then this view is already
1135             // connected so we want to restart it.
1136             if (mCurrentTextBoxAttribute == null) {
1137                 controlFlags |= CONTROL_START_INITIAL;
1138             }
1139 
1140             // Hook 'em up and let 'er rip.
1141             mCurrentTextBoxAttribute = tba;
1142             mServedConnecting = false;
1143             // Notify the served view that its previous input connection is finished
1144             notifyInputConnectionFinished();
1145             mServedInputConnection = ic;
1146             ControlledInputConnectionWrapper servedContext;
1147             if (ic != null) {
1148                 mCursorSelStart = tba.initialSelStart;
1149                 mCursorSelEnd = tba.initialSelEnd;
1150                 mCursorCandStart = -1;
1151                 mCursorCandEnd = -1;
1152                 mCursorRect.setEmpty();
1153                 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
1154             } else {
1155                 servedContext = null;
1156             }
1157             if (mServedInputConnectionWrapper != null) {
1158                 mServedInputConnectionWrapper.deactivate();
1159             }
1160             mServedInputConnectionWrapper = servedContext;
1161 
1162             try {
1163                 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
1164                         + ic + " tba=" + tba + " controlFlags=#"
1165                         + Integer.toHexString(controlFlags));
1166                 InputBindResult res;
1167                 if (windowGainingFocus != null) {
1168                     res = mService.windowGainedFocus(mClient, windowGainingFocus,
1169                             controlFlags, softInputMode, windowFlags,
1170                             tba, servedContext);
1171                 } else {
1172                     res = mService.startInput(mClient,
1173                             servedContext, tba, controlFlags);
1174                 }
1175                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
1176                 if (res != null) {
1177                     if (res.id != null) {
1178                         setInputChannelLocked(res.channel);
1179                         mBindSequence = res.sequence;
1180                         mCurMethod = res.method;
1181                         mCurId = res.id;
1182                     } else {
1183                         if (res.channel != null && res.channel != mCurChannel) {
1184                             res.channel.dispose();
1185                         }
1186                         if (mCurMethod == null) {
1187                             // This means there is no input method available.
1188                             if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1189                             return true;
1190                         }
1191                     }
1192                 }
1193                 if (mCurMethod != null && mCompletions != null) {
1194                     try {
1195                         mCurMethod.displayCompletions(mCompletions);
1196                     } catch (RemoteException e) {
1197                     }
1198                 }
1199             } catch (RemoteException e) {
1200                 Log.w(TAG, "IME died: " + mCurId, e);
1201             }
1202         }
1203 
1204         return true;
1205     }
1206 
1207     /**
1208      * When the focused window is dismissed, this method is called to finish the
1209      * input method started before.
1210      * @hide
1211      */
windowDismissed(IBinder appWindowToken)1212     public void windowDismissed(IBinder appWindowToken) {
1213         checkFocus();
1214         synchronized (mH) {
1215             if (mServedView != null &&
1216                     mServedView.getWindowToken() == appWindowToken) {
1217                 finishInputLocked();
1218             }
1219         }
1220     }
1221 
1222     /**
1223      * Call this when a view receives focus.
1224      * @hide
1225      */
focusIn(View view)1226     public void focusIn(View view) {
1227         synchronized (mH) {
1228             focusInLocked(view);
1229         }
1230     }
1231 
focusInLocked(View view)1232     void focusInLocked(View view) {
1233         if (DEBUG) Log.v(TAG, "focusIn: " + view);
1234 
1235         if (mCurRootView != view.getRootView()) {
1236             // This is a request from a window that isn't in the window with
1237             // IME focus, so ignore it.
1238             if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1239             return;
1240         }
1241 
1242         mNextServedView = view;
1243         scheduleCheckFocusLocked(view);
1244     }
1245 
1246     /**
1247      * Call this when a view loses focus.
1248      * @hide
1249      */
focusOut(View view)1250     public void focusOut(View view) {
1251         synchronized (mH) {
1252             if (DEBUG) Log.v(TAG, "focusOut: " + view
1253                     + " mServedView=" + mServedView
1254                     + " winFocus=" + view.hasWindowFocus());
1255             if (mServedView != view) {
1256                 // The following code would auto-hide the IME if we end up
1257                 // with no more views with focus.  This can happen, however,
1258                 // whenever we go into touch mode, so it ends up hiding
1259                 // at times when we don't really want it to.  For now it
1260                 // seems better to just turn it all off.
1261                 if (false && view.hasWindowFocus()) {
1262                     mNextServedView = null;
1263                     scheduleCheckFocusLocked(view);
1264                 }
1265             }
1266         }
1267     }
1268 
scheduleCheckFocusLocked(View view)1269     static void scheduleCheckFocusLocked(View view) {
1270         ViewRootImpl viewRootImpl = view.getViewRootImpl();
1271         if (viewRootImpl != null) {
1272             viewRootImpl.dispatchCheckFocus();
1273         }
1274     }
1275 
1276     /**
1277      * @hide
1278      */
checkFocus()1279     public void checkFocus() {
1280         if (checkFocusNoStartInput(false, true)) {
1281             startInputInner(null, 0, 0, 0);
1282         }
1283     }
1284 
checkFocusNoStartInput(boolean forceNewFocus, boolean finishComposingText)1285     private boolean checkFocusNoStartInput(boolean forceNewFocus, boolean finishComposingText) {
1286         // This is called a lot, so short-circuit before locking.
1287         if (mServedView == mNextServedView && !forceNewFocus) {
1288             return false;
1289         }
1290 
1291         InputConnection ic = null;
1292         synchronized (mH) {
1293             if (mServedView == mNextServedView && !forceNewFocus) {
1294                 return false;
1295             }
1296             if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1297                     + " next=" + mNextServedView
1298                     + " forceNewFocus=" + forceNewFocus
1299                     + " package="
1300                     + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
1301 
1302             if (mNextServedView == null) {
1303                 finishInputLocked();
1304                 // In this case, we used to have a focused view on the window,
1305                 // but no longer do.  We should make sure the input method is
1306                 // no longer shown, since it serves no purpose.
1307                 closeCurrentInput();
1308                 return false;
1309             }
1310 
1311             ic = mServedInputConnection;
1312 
1313             mServedView = mNextServedView;
1314             mCurrentTextBoxAttribute = null;
1315             mCompletions = null;
1316             mServedConnecting = true;
1317         }
1318 
1319         if (finishComposingText && ic != null) {
1320             ic.finishComposingText();
1321         }
1322 
1323         return true;
1324     }
1325 
closeCurrentInput()1326     void closeCurrentInput() {
1327         try {
1328             mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
1329         } catch (RemoteException e) {
1330         }
1331     }
1332 
1333     /**
1334      * Called by ViewAncestor when its window gets input focus.
1335      * @hide
1336      */
onWindowFocus(View rootView, View focusedView, int softInputMode, boolean first, int windowFlags)1337     public void onWindowFocus(View rootView, View focusedView, int softInputMode,
1338             boolean first, int windowFlags) {
1339         boolean forceNewFocus = false;
1340         synchronized (mH) {
1341             if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1342                     + " softInputMode=" + softInputMode
1343                     + " first=" + first + " flags=#"
1344                     + Integer.toHexString(windowFlags));
1345             if (mHasBeenInactive) {
1346                 if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
1347                 mHasBeenInactive = false;
1348                 forceNewFocus = true;
1349             }
1350             focusInLocked(focusedView != null ? focusedView : rootView);
1351         }
1352 
1353         int controlFlags = 0;
1354         if (focusedView != null) {
1355             controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
1356             if (focusedView.onCheckIsTextEditor()) {
1357                 controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
1358             }
1359         }
1360         if (first) {
1361             controlFlags |= CONTROL_WINDOW_FIRST;
1362         }
1363 
1364         if (checkFocusNoStartInput(forceNewFocus, true)) {
1365             // We need to restart input on the current focus view.  This
1366             // should be done in conjunction with telling the system service
1367             // about the window gaining focus, to help make the transition
1368             // smooth.
1369             if (startInputInner(rootView.getWindowToken(),
1370                     controlFlags, softInputMode, windowFlags)) {
1371                 return;
1372             }
1373         }
1374 
1375         // For some reason we didn't do a startInput + windowFocusGain, so
1376         // we'll just do a window focus gain and call it a day.
1377         synchronized (mH) {
1378             try {
1379                 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
1380                 mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1381                         controlFlags, softInputMode, windowFlags, null, null);
1382             } catch (RemoteException e) {
1383             }
1384         }
1385     }
1386 
1387     /** @hide */
startGettingWindowFocus(View rootView)1388     public void startGettingWindowFocus(View rootView) {
1389         synchronized (mH) {
1390             mCurRootView = rootView;
1391         }
1392     }
1393 
1394     /**
1395      * Report the current selection range.
1396      */
updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd)1397     public void updateSelection(View view, int selStart, int selEnd,
1398             int candidatesStart, int candidatesEnd) {
1399         checkFocus();
1400         synchronized (mH) {
1401             if ((mServedView != view && (mServedView == null
1402                         || !mServedView.checkInputConnectionProxy(view)))
1403                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1404                 return;
1405             }
1406 
1407             if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1408                     || mCursorCandStart != candidatesStart
1409                     || mCursorCandEnd != candidatesEnd) {
1410                 if (DEBUG) Log.d(TAG, "updateSelection");
1411 
1412                 try {
1413                     if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
1414                     mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
1415                             selStart, selEnd, candidatesStart, candidatesEnd);
1416                     mCursorSelStart = selStart;
1417                     mCursorSelEnd = selEnd;
1418                     mCursorCandStart = candidatesStart;
1419                     mCursorCandEnd = candidatesEnd;
1420                 } catch (RemoteException e) {
1421                     Log.w(TAG, "IME died: " + mCurId, e);
1422                 }
1423             }
1424         }
1425     }
1426 
1427     /**
1428      * Notify the event when the user tapped or clicked the text view.
1429      */
viewClicked(View view)1430     public void viewClicked(View view) {
1431         final boolean focusChanged = mServedView != mNextServedView;
1432         checkFocus();
1433         synchronized (mH) {
1434             if ((mServedView != view && (mServedView == null
1435                     || !mServedView.checkInputConnectionProxy(view)))
1436                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1437                 return;
1438             }
1439             try {
1440                 if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
1441                 mCurMethod.viewClicked(focusChanged);
1442             } catch (RemoteException e) {
1443                 Log.w(TAG, "IME died: " + mCurId, e);
1444             }
1445         }
1446     }
1447 
1448     /**
1449      * Returns true if the current input method wants to watch the location
1450      * of the input editor's cursor in its window.
1451      */
isWatchingCursor(View view)1452     public boolean isWatchingCursor(View view) {
1453         return false;
1454     }
1455 
1456     /**
1457      * Report the current cursor location in its window.
1458      */
updateCursor(View view, int left, int top, int right, int bottom)1459     public void updateCursor(View view, int left, int top, int right, int bottom) {
1460         checkFocus();
1461         synchronized (mH) {
1462             if ((mServedView != view && (mServedView == null
1463                         || !mServedView.checkInputConnectionProxy(view)))
1464                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1465                 return;
1466             }
1467 
1468             mTmpCursorRect.set(left, top, right, bottom);
1469             if (!mCursorRect.equals(mTmpCursorRect)) {
1470                 if (DEBUG) Log.d(TAG, "updateCursor");
1471 
1472                 try {
1473                     if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1474                     mCurMethod.updateCursor(mTmpCursorRect);
1475                     mCursorRect.set(mTmpCursorRect);
1476                 } catch (RemoteException e) {
1477                     Log.w(TAG, "IME died: " + mCurId, e);
1478                 }
1479             }
1480         }
1481     }
1482 
1483     /**
1484      * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1485      * InputMethodSession.appPrivateCommand()} on the current Input Method.
1486      * @param view Optional View that is sending the command, or null if
1487      * you want to send the command regardless of the view that is attached
1488      * to the input method.
1489      * @param action Name of the command to be performed.  This <em>must</em>
1490      * be a scoped name, i.e. prefixed with a package name you own, so that
1491      * different developers will not create conflicting commands.
1492      * @param data Any data to include with the command.
1493      */
sendAppPrivateCommand(View view, String action, Bundle data)1494     public void sendAppPrivateCommand(View view, String action, Bundle data) {
1495         checkFocus();
1496         synchronized (mH) {
1497             if ((mServedView != view && (mServedView == null
1498                         || !mServedView.checkInputConnectionProxy(view)))
1499                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1500                 return;
1501             }
1502             try {
1503                 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1504                 mCurMethod.appPrivateCommand(action, data);
1505             } catch (RemoteException e) {
1506                 Log.w(TAG, "IME died: " + mCurId, e);
1507             }
1508         }
1509     }
1510 
1511     /**
1512      * Force switch to a new input method component. This can only be called
1513      * from an application or a service which has a token of the currently active input method.
1514      * @param token Supplies the identifying token given to an input method
1515      * when it was started, which allows it to perform this operation on
1516      * itself.
1517      * @param id The unique identifier for the new input method to be switched to.
1518      */
setInputMethod(IBinder token, String id)1519     public void setInputMethod(IBinder token, String id) {
1520         try {
1521             mService.setInputMethod(token, id);
1522         } catch (RemoteException e) {
1523             throw new RuntimeException(e);
1524         }
1525     }
1526 
1527     /**
1528      * Force switch to a new input method and subtype. This can only be called
1529      * from an application or a service which has a token of the currently active input method.
1530      * @param token Supplies the identifying token given to an input method
1531      * when it was started, which allows it to perform this operation on
1532      * itself.
1533      * @param id The unique identifier for the new input method to be switched to.
1534      * @param subtype The new subtype of the new input method to be switched to.
1535      */
setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype)1536     public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
1537         try {
1538             mService.setInputMethodAndSubtype(token, id, subtype);
1539         } catch (RemoteException e) {
1540             throw new RuntimeException(e);
1541         }
1542     }
1543 
1544     /**
1545      * Close/hide the input method's soft input area, so the user no longer
1546      * sees it or can interact with it.  This can only be called
1547      * from the currently active input method, as validated by the given token.
1548      *
1549      * @param token Supplies the identifying token given to an input method
1550      * when it was started, which allows it to perform this operation on
1551      * itself.
1552      * @param flags Provides additional operating flags.  Currently may be
1553      * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1554      * {@link #HIDE_NOT_ALWAYS} bit set.
1555      */
hideSoftInputFromInputMethod(IBinder token, int flags)1556     public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1557         try {
1558             mService.hideMySoftInput(token, flags);
1559         } catch (RemoteException e) {
1560             throw new RuntimeException(e);
1561         }
1562     }
1563 
1564     /**
1565      * Show the input method's soft input area, so the user
1566      * sees the input method window and can interact with it.
1567      * This can only be called from the currently active input method,
1568      * as validated by the given token.
1569      *
1570      * @param token Supplies the identifying token given to an input method
1571      * when it was started, which allows it to perform this operation on
1572      * itself.
1573      * @param flags Provides additional operating flags.  Currently may be
1574      * 0 or have the {@link #SHOW_IMPLICIT} or
1575      * {@link #SHOW_FORCED} bit set.
1576      */
showSoftInputFromInputMethod(IBinder token, int flags)1577     public void showSoftInputFromInputMethod(IBinder token, int flags) {
1578         try {
1579             mService.showMySoftInput(token, flags);
1580         } catch (RemoteException e) {
1581             throw new RuntimeException(e);
1582         }
1583     }
1584 
1585     /**
1586      * Dispatches an input event to the IME.
1587      *
1588      * Returns {@link #DISPATCH_HANDLED} if the event was handled.
1589      * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
1590      * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
1591      * callback will be invoked later.
1592      *
1593      * @hide
1594      */
dispatchInputEvent(InputEvent event, Object token, FinishedInputEventCallback callback, Handler handler)1595     public int dispatchInputEvent(InputEvent event, Object token,
1596             FinishedInputEventCallback callback, Handler handler) {
1597         synchronized (mH) {
1598             if (mCurMethod != null) {
1599                 if (event instanceof KeyEvent) {
1600                     KeyEvent keyEvent = (KeyEvent)event;
1601                     if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
1602                             && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
1603                             && keyEvent.getRepeatCount() == 0) {
1604                         showInputMethodPickerLocked();
1605                         return DISPATCH_HANDLED;
1606                     }
1607                 }
1608 
1609                 if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
1610 
1611                 PendingEvent p = obtainPendingEventLocked(
1612                         event, token, mCurId, callback, handler);
1613                 if (mMainLooper.isCurrentThread()) {
1614                     // Already running on the IMM thread so we can send the event immediately.
1615                     return sendInputEventOnMainLooperLocked(p);
1616                 }
1617 
1618                 // Post the event to the IMM thread.
1619                 Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
1620                 msg.setAsynchronous(true);
1621                 mH.sendMessage(msg);
1622                 return DISPATCH_IN_PROGRESS;
1623             }
1624         }
1625         return DISPATCH_NOT_HANDLED;
1626     }
1627 
1628     // Must be called on the main looper
sendInputEventAndReportResultOnMainLooper(PendingEvent p)1629     void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
1630         final boolean handled;
1631         synchronized (mH) {
1632             int result = sendInputEventOnMainLooperLocked(p);
1633             if (result == DISPATCH_IN_PROGRESS) {
1634                 return;
1635             }
1636 
1637             handled = (result == DISPATCH_HANDLED);
1638         }
1639 
1640         invokeFinishedInputEventCallback(p, handled);
1641     }
1642 
1643     // Must be called on the main looper
sendInputEventOnMainLooperLocked(PendingEvent p)1644     int sendInputEventOnMainLooperLocked(PendingEvent p) {
1645         if (mCurChannel != null) {
1646             if (mCurSender == null) {
1647                 mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
1648             }
1649 
1650             final InputEvent event = p.mEvent;
1651             final int seq = event.getSequenceNumber();
1652             if (mCurSender.sendInputEvent(seq, event)) {
1653                 mPendingEvents.put(seq, p);
1654                 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
1655                         mPendingEvents.size());
1656 
1657                 Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
1658                 msg.setAsynchronous(true);
1659                 mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
1660                 return DISPATCH_IN_PROGRESS;
1661             }
1662 
1663             Log.w(TAG, "Unable to send input event to IME: "
1664                     + mCurId + " dropping: " + event);
1665         }
1666         return DISPATCH_NOT_HANDLED;
1667     }
1668 
finishedInputEvent(int seq, boolean handled, boolean timeout)1669     void finishedInputEvent(int seq, boolean handled, boolean timeout) {
1670         final PendingEvent p;
1671         synchronized (mH) {
1672             int index = mPendingEvents.indexOfKey(seq);
1673             if (index < 0) {
1674                 return; // spurious, event already finished or timed out
1675             }
1676 
1677             p = mPendingEvents.valueAt(index);
1678             mPendingEvents.removeAt(index);
1679             Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
1680 
1681             if (timeout) {
1682                 Log.w(TAG, "Timeout waiting for IME to handle input event after "
1683                         + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
1684             } else {
1685                 mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
1686             }
1687         }
1688 
1689         invokeFinishedInputEventCallback(p, handled);
1690     }
1691 
1692     // Assumes the event has already been removed from the queue.
invokeFinishedInputEventCallback(PendingEvent p, boolean handled)1693     void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
1694         p.mHandled = handled;
1695         if (p.mHandler.getLooper().isCurrentThread()) {
1696             // Already running on the callback handler thread so we can send the
1697             // callback immediately.
1698             p.run();
1699         } else {
1700             // Post the event to the callback handler thread.
1701             // In this case, the callback will be responsible for recycling the event.
1702             Message msg = Message.obtain(p.mHandler, p);
1703             msg.setAsynchronous(true);
1704             msg.sendToTarget();
1705         }
1706     }
1707 
flushPendingEventsLocked()1708     private void flushPendingEventsLocked() {
1709         mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
1710 
1711         final int count = mPendingEvents.size();
1712         for (int i = 0; i < count; i++) {
1713             int seq = mPendingEvents.keyAt(i);
1714             Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
1715             msg.setAsynchronous(true);
1716             msg.sendToTarget();
1717         }
1718     }
1719 
obtainPendingEventLocked(InputEvent event, Object token, String inputMethodId, FinishedInputEventCallback callback, Handler handler)1720     private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
1721             String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
1722         PendingEvent p = mPendingEventPool.acquire();
1723         if (p == null) {
1724             p = new PendingEvent();
1725         }
1726         p.mEvent = event;
1727         p.mToken = token;
1728         p.mInputMethodId = inputMethodId;
1729         p.mCallback = callback;
1730         p.mHandler = handler;
1731         return p;
1732     }
1733 
recyclePendingEventLocked(PendingEvent p)1734     private void recyclePendingEventLocked(PendingEvent p) {
1735         p.recycle();
1736         mPendingEventPool.release(p);
1737     }
1738 
showInputMethodPicker()1739     public void showInputMethodPicker() {
1740         synchronized (mH) {
1741             showInputMethodPickerLocked();
1742         }
1743     }
1744 
showInputMethodPickerLocked()1745     private void showInputMethodPickerLocked() {
1746         try {
1747             mService.showInputMethodPickerFromClient(mClient);
1748         } catch (RemoteException e) {
1749             Log.w(TAG, "IME died: " + mCurId, e);
1750         }
1751     }
1752 
1753     /**
1754      * Show the settings for enabling subtypes of the specified input method.
1755      * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
1756      * subtypes of all input methods will be shown.
1757      */
showInputMethodAndSubtypeEnabler(String imiId)1758     public void showInputMethodAndSubtypeEnabler(String imiId) {
1759         synchronized (mH) {
1760             try {
1761                 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
1762             } catch (RemoteException e) {
1763                 Log.w(TAG, "IME died: " + mCurId, e);
1764             }
1765         }
1766     }
1767 
1768     /**
1769      * Returns the current input method subtype. This subtype is one of the subtypes in
1770      * the current input method. This method returns null when the current input method doesn't
1771      * have any input method subtype.
1772      */
getCurrentInputMethodSubtype()1773     public InputMethodSubtype getCurrentInputMethodSubtype() {
1774         synchronized (mH) {
1775             try {
1776                 return mService.getCurrentInputMethodSubtype();
1777             } catch (RemoteException e) {
1778                 Log.w(TAG, "IME died: " + mCurId, e);
1779                 return null;
1780             }
1781         }
1782     }
1783 
1784     /**
1785      * Switch to a new input method subtype of the current input method.
1786      * @param subtype A new input method subtype to switch.
1787      * @return true if the current subtype was successfully switched. When the specified subtype is
1788      * null, this method returns false.
1789      */
setCurrentInputMethodSubtype(InputMethodSubtype subtype)1790     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
1791         synchronized (mH) {
1792             try {
1793                 return mService.setCurrentInputMethodSubtype(subtype);
1794             } catch (RemoteException e) {
1795                 Log.w(TAG, "IME died: " + mCurId, e);
1796                 return false;
1797             }
1798         }
1799     }
1800 
1801     /**
1802      * Returns a map of all shortcut input method info and their subtypes.
1803      */
getShortcutInputMethodsAndSubtypes()1804     public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
1805         synchronized (mH) {
1806             HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
1807                     new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
1808             try {
1809                 // TODO: We should change the return type from List<Object> to List<Parcelable>
1810                 List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
1811                 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
1812                 ArrayList<InputMethodSubtype> subtypes = null;
1813                 final int N = info.size();
1814                 if (info != null && N > 0) {
1815                     for (int i = 0; i < N; ++i) {
1816                         Object o = info.get(i);
1817                         if (o instanceof InputMethodInfo) {
1818                             if (ret.containsKey(o)) {
1819                                 Log.e(TAG, "IMI list already contains the same InputMethod.");
1820                                 break;
1821                             }
1822                             subtypes = new ArrayList<InputMethodSubtype>();
1823                             ret.put((InputMethodInfo)o, subtypes);
1824                         } else if (subtypes != null && o instanceof InputMethodSubtype) {
1825                             subtypes.add((InputMethodSubtype)o);
1826                         }
1827                     }
1828                 }
1829             } catch (RemoteException e) {
1830                 Log.w(TAG, "IME died: " + mCurId, e);
1831             }
1832             return ret;
1833         }
1834     }
1835 
1836     /**
1837      * Force switch to the last used input method and subtype. If the last input method didn't have
1838      * any subtypes, the framework will simply switch to the last input method with no subtype
1839      * specified.
1840      * @param imeToken Supplies the identifying token given to an input method when it was started,
1841      * which allows it to perform this operation on itself.
1842      * @return true if the current input method and subtype was successfully switched to the last
1843      * used input method and subtype.
1844      */
switchToLastInputMethod(IBinder imeToken)1845     public boolean switchToLastInputMethod(IBinder imeToken) {
1846         synchronized (mH) {
1847             try {
1848                 return mService.switchToLastInputMethod(imeToken);
1849             } catch (RemoteException e) {
1850                 Log.w(TAG, "IME died: " + mCurId, e);
1851                 return false;
1852             }
1853         }
1854     }
1855 
1856     /**
1857      * Force switch to the next input method and subtype. If there is no IME enabled except
1858      * current IME and subtype, do nothing.
1859      * @param imeToken Supplies the identifying token given to an input method when it was started,
1860      * which allows it to perform this operation on itself.
1861      * @param onlyCurrentIme if true, the framework will find the next subtype which
1862      * belongs to the current IME
1863      * @return true if the current input method and subtype was successfully switched to the next
1864      * input method and subtype.
1865      */
switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme)1866     public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
1867         synchronized (mH) {
1868             try {
1869                 return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
1870             } catch (RemoteException e) {
1871                 Log.w(TAG, "IME died: " + mCurId, e);
1872                 return false;
1873             }
1874         }
1875     }
1876 
1877     /**
1878      * Set additional input method subtypes. Only a process which shares the same uid with the IME
1879      * can add additional input method subtypes to the IME.
1880      * Please note that a subtype's status is stored in the system.
1881      * For example, enabled subtypes are remembered by the framework even after they are removed
1882      * by using this method. If you re-add the same subtypes again,
1883      * they will just get enabled. If you want to avoid such conflicts, for instance, you may
1884      * want to create a "different" new subtype even with the same locale and mode,
1885      * by changing its extra value. The different subtype won't get affected by the stored past
1886      * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer
1887      * to the current implementation.)
1888      * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
1889      * @param subtypes subtypes will be added as additional subtypes of the current input method.
1890      */
setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes)1891     public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
1892         synchronized (mH) {
1893             try {
1894                 mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
1895             } catch (RemoteException e) {
1896                 Log.w(TAG, "IME died: " + mCurId, e);
1897             }
1898         }
1899     }
1900 
getLastInputMethodSubtype()1901     public InputMethodSubtype getLastInputMethodSubtype() {
1902         synchronized (mH) {
1903             try {
1904                 return mService.getLastInputMethodSubtype();
1905             } catch (RemoteException e) {
1906                 Log.w(TAG, "IME died: " + mCurId, e);
1907                 return null;
1908             }
1909         }
1910     }
1911 
doDump(FileDescriptor fd, PrintWriter fout, String[] args)1912     void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1913         final Printer p = new PrintWriterPrinter(fout);
1914         p.println("Input method client state for " + this + ":");
1915 
1916         p.println("  mService=" + mService);
1917         p.println("  mMainLooper=" + mMainLooper);
1918         p.println("  mIInputContext=" + mIInputContext);
1919         p.println("  mActive=" + mActive
1920                 + " mHasBeenInactive=" + mHasBeenInactive
1921                 + " mBindSequence=" + mBindSequence
1922                 + " mCurId=" + mCurId);
1923         p.println("  mCurMethod=" + mCurMethod);
1924         p.println("  mCurRootView=" + mCurRootView);
1925         p.println("  mServedView=" + mServedView);
1926         p.println("  mNextServedView=" + mNextServedView);
1927         p.println("  mServedConnecting=" + mServedConnecting);
1928         if (mCurrentTextBoxAttribute != null) {
1929             p.println("  mCurrentTextBoxAttribute:");
1930             mCurrentTextBoxAttribute.dump(p, "    ");
1931         } else {
1932             p.println("  mCurrentTextBoxAttribute: null");
1933         }
1934         p.println("  mServedInputConnection=" + mServedInputConnection);
1935         p.println("  mCompletions=" + mCompletions);
1936         p.println("  mCursorRect=" + mCursorRect);
1937         p.println("  mCursorSelStart=" + mCursorSelStart
1938                 + " mCursorSelEnd=" + mCursorSelEnd
1939                 + " mCursorCandStart=" + mCursorCandStart
1940                 + " mCursorCandEnd=" + mCursorCandEnd);
1941     }
1942 
1943     /**
1944      * Callback that is invoked when an input event that was dispatched to
1945      * the IME has been finished.
1946      * @hide
1947      */
1948     public interface FinishedInputEventCallback {
onFinishedInputEvent(Object token, boolean handled)1949         public void onFinishedInputEvent(Object token, boolean handled);
1950     }
1951 
1952     private final class ImeInputEventSender extends InputEventSender {
ImeInputEventSender(InputChannel inputChannel, Looper looper)1953         public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
1954             super(inputChannel, looper);
1955         }
1956 
1957         @Override
onInputEventFinished(int seq, boolean handled)1958         public void onInputEventFinished(int seq, boolean handled) {
1959             finishedInputEvent(seq, handled, false);
1960         }
1961     }
1962 
1963     private final class PendingEvent implements Runnable {
1964         public InputEvent mEvent;
1965         public Object mToken;
1966         public String mInputMethodId;
1967         public FinishedInputEventCallback mCallback;
1968         public Handler mHandler;
1969         public boolean mHandled;
1970 
recycle()1971         public void recycle() {
1972             mEvent = null;
1973             mToken = null;
1974             mInputMethodId = null;
1975             mCallback = null;
1976             mHandler = null;
1977             mHandled = false;
1978         }
1979 
1980         @Override
run()1981         public void run() {
1982             mCallback.onFinishedInputEvent(mToken, mHandled);
1983 
1984             synchronized (mH) {
1985                 recyclePendingEventLocked(this);
1986             }
1987         }
1988     }
1989 }
1990