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