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