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