InputMethodManager.java revision 06487a58be22b100daf3f950b9a1d25c3ea42aa2
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 android.content.Context;
20import android.graphics.Rect;
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Looper;
25import android.os.Message;
26import android.os.RemoteException;
27import android.os.ResultReceiver;
28import android.os.ServiceManager;
29import android.util.Log;
30import android.util.PrintWriterPrinter;
31import android.util.Printer;
32import android.view.KeyEvent;
33import android.view.MotionEvent;
34import android.view.View;
35import android.view.ViewRoot;
36import android.view.inputmethod.InputMethodSubtype;
37
38import com.android.internal.os.HandlerCaller;
39import com.android.internal.view.IInputConnectionWrapper;
40import com.android.internal.view.IInputContext;
41import com.android.internal.view.IInputMethodCallback;
42import com.android.internal.view.IInputMethodClient;
43import com.android.internal.view.IInputMethodManager;
44import com.android.internal.view.IInputMethodSession;
45import com.android.internal.view.InputBindResult;
46
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
49import java.util.List;
50import java.util.concurrent.CountDownLatch;
51import java.util.concurrent.TimeUnit;
52
53/**
54 * Central system API to the overall input method framework (IMF) architecture,
55 * which arbitrates interaction between applications and the current input method.
56 * You can retrieve an instance of this interface with
57 * {@link Context#getSystemService(String) Context.getSystemService()}.
58 *
59 * <p>Topics covered here:
60 * <ol>
61 * <li><a href="#ArchitectureOverview">Architecture Overview</a>
62 * </ol>
63 *
64 * <a name="ArchitectureOverview"></a>
65 * <h3>Architecture Overview</h3>
66 *
67 * <p>There are three primary parties involved in the input method
68 * framework (IMF) architecture:</p>
69 *
70 * <ul>
71 * <li> The <strong>input method manager</strong> as expressed by this class
72 * is the central point of the system that manages interaction between all
73 * other parts.  It is expressed as the client-side API here which exists
74 * in each application context and communicates with a global system service
75 * that manages the interaction across all processes.
76 * <li> An <strong>input method (IME)</strong> implements a particular
77 * interaction model allowing the user to generate text.  The system binds
78 * to the current input method that is use, causing it to be created and run,
79 * and tells it when to hide and show its UI.  Only one IME is running at a time.
80 * <li> Multiple <strong>client applications</strong> arbitrate with the input
81 * method manager for input focus and control over the state of the IME.  Only
82 * one such client is ever active (working with the IME) at a time.
83 * </ul>
84 *
85 *
86 * <a name="Applications"></a>
87 * <h3>Applications</h3>
88 *
89 * <p>In most cases, applications that are using the standard
90 * {@link android.widget.TextView} or its subclasses will have little they need
91 * to do to work well with soft input methods.  The main things you need to
92 * be aware of are:</p>
93 *
94 * <ul>
95 * <li> Properly set the {@link android.R.attr#inputType} if your editable
96 * text views, so that the input method will have enough context to help the
97 * user in entering text into them.
98 * <li> Deal well with losing screen space when the input method is
99 * displayed.  Ideally an application should handle its window being resized
100 * smaller, but it can rely on the system performing panning of the window
101 * if needed.  You should set the {@link android.R.attr#windowSoftInputMode}
102 * attribute on your activity or the corresponding values on windows you
103 * create to help the system determine whether to pan or resize (it will
104 * try to determine this automatically but may get it wrong).
105 * <li> You can also control the preferred soft input state (open, closed, etc)
106 * for your window using the same {@link android.R.attr#windowSoftInputMode}
107 * attribute.
108 * </ul>
109 *
110 * <p>More finer-grained control is available through the APIs here to directly
111 * interact with the IMF and its IME -- either showing or hiding the input
112 * area, letting the user pick an input method, etc.</p>
113 *
114 * <p>For the rare people amongst us writing their own text editors, you
115 * will need to implement {@link android.view.View#onCreateInputConnection}
116 * to return a new instance of your own {@link InputConnection} interface
117 * allowing the IME to interact with your editor.</p>
118 *
119 *
120 * <a name="InputMethods"></a>
121 * <h3>Input Methods</h3>
122 *
123 * <p>An input method (IME) is implemented
124 * as a {@link android.app.Service}, typically deriving from
125 * {@link android.inputmethodservice.InputMethodService}.  It must provide
126 * the core {@link InputMethod} interface, though this is normally handled by
127 * {@link android.inputmethodservice.InputMethodService} and implementors will
128 * only need to deal with the higher-level API there.</p>
129 *
130 * See the {@link android.inputmethodservice.InputMethodService} class for
131 * more information on implementing IMEs.
132 *
133 *
134 * <a name="Security"></a>
135 * <h3>Security</h3>
136 *
137 * <p>There are a lot of security issues associated with input methods,
138 * since they essentially have freedom to completely drive the UI and monitor
139 * everything the user enters.  The Android input method framework also allows
140 * arbitrary third party IMEs, so care must be taken to restrict their
141 * selection and interactions.</p>
142 *
143 * <p>Here are some key points about the security architecture behind the
144 * IMF:</p>
145 *
146 * <ul>
147 * <li> <p>Only the system is allowed to directly access an IME's
148 * {@link InputMethod} interface, via the
149 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission.  This is
150 * enforced in the system by not binding to an input method service that does
151 * not require this permission, so the system can guarantee no other untrusted
152 * clients are accessing the current input method outside of its control.</p>
153 *
154 * <li> <p>There may be many client processes of the IMF, but only one may
155 * be active at a time.  The inactive clients can not interact with key
156 * parts of the IMF through the mechanisms described below.</p>
157 *
158 * <li> <p>Clients of an input method are only given access to its
159 * {@link InputMethodSession} interface.  One instance of this interface is
160 * created for each client, and only calls from the session associated with
161 * the active client will be processed by the current IME.  This is enforced
162 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
163 * IMEs, but must be explicitly handled by an IME that is customizing the
164 * raw {@link InputMethodSession} implementation.</p>
165 *
166 * <li> <p>Only the active client's {@link InputConnection} will accept
167 * operations.  The IMF tells each client process whether it is active, and
168 * the framework enforces that in inactive processes calls on to the current
169 * InputConnection will be ignored.  This ensures that the current IME can
170 * only deliver events and text edits to the UI that the user sees as
171 * being in focus.</p>
172 *
173 * <li> <p>An IME can never interact with an {@link InputConnection} while
174 * the screen is off.  This is enforced by making all clients inactive while
175 * the screen is off, and prevents bad IMEs from driving the UI when the user
176 * can not be aware of its behavior.</p>
177 *
178 * <li> <p>A client application can ask that the system let the user pick a
179 * new IME, but can not programmatically switch to one itself.  This avoids
180 * malicious applications from switching the user to their own IME, which
181 * remains running when the user navigates away to another application.  An
182 * IME, on the other hand, <em>is</em> allowed to programmatically switch
183 * the system to another IME, since it already has full control of user
184 * input.</p>
185 *
186 * <li> <p>The user must explicitly enable a new IME in settings before
187 * they can switch to it, to confirm with the system that they know about it
188 * and want to make it available for use.</p>
189 * </ul>
190 */
191public final class InputMethodManager {
192    static final boolean DEBUG = false;
193    static final String TAG = "InputMethodManager";
194
195    static final Object mInstanceSync = new Object();
196    static InputMethodManager mInstance;
197
198    final IInputMethodManager mService;
199    final Looper mMainLooper;
200
201    // For scheduling work on the main thread.  This also serves as our
202    // global lock.
203    final H mH;
204
205    // Our generic input connection if the current target does not have its own.
206    final IInputContext mIInputContext;
207
208    /**
209     * True if this input method client is active, initially false.
210     */
211    boolean mActive = false;
212
213    /**
214     * Set whenever this client becomes inactive, to know we need to reset
215     * state with the IME then next time we receive focus.
216     */
217    boolean mHasBeenInactive = true;
218
219    /**
220     * As reported by IME through InputConnection.
221     */
222    boolean mFullscreenMode;
223
224    // -----------------------------------------------------------
225
226    /**
227     * This is the root view of the overall window that currently has input
228     * method focus.
229     */
230    View mCurRootView;
231    /**
232     * This is the view that should currently be served by an input method,
233     * regardless of the state of setting that up.
234     */
235    View mServedView;
236    /**
237     * This is then next view that will be served by the input method, when
238     * we get around to updating things.
239     */
240    View mNextServedView;
241    /**
242     * True if we should restart input in the next served view, even if the
243     * view hasn't actually changed from the current serve view.
244     */
245    boolean mNextServedNeedsStart;
246    /**
247     * This is set when we are in the process of connecting, to determine
248     * when we have actually finished.
249     */
250    boolean mServedConnecting;
251    /**
252     * This is non-null when we have connected the served view; it holds
253     * the attributes that were last retrieved from the served view and given
254     * to the input connection.
255     */
256    EditorInfo mCurrentTextBoxAttribute;
257    /**
258     * The InputConnection that was last retrieved from the served view.
259     */
260    InputConnection mServedInputConnection;
261    /**
262     * The completions that were last provided by the served view.
263     */
264    CompletionInfo[] mCompletions;
265
266    // Cursor position on the screen.
267    Rect mTmpCursorRect = new Rect();
268    Rect mCursorRect = new Rect();
269    int mCursorSelStart;
270    int mCursorSelEnd;
271    int mCursorCandStart;
272    int mCursorCandEnd;
273
274    // -----------------------------------------------------------
275
276    /**
277     * Sequence number of this binding, as returned by the server.
278     */
279    int mBindSequence = -1;
280    /**
281     * ID of the method we are bound to.
282     */
283    String mCurId;
284    /**
285     * The actual instance of the method to make calls on it.
286     */
287    IInputMethodSession mCurMethod;
288
289    // -----------------------------------------------------------
290
291    static final int MSG_DUMP = 1;
292    static final int MSG_BIND = 2;
293    static final int MSG_UNBIND = 3;
294    static final int MSG_SET_ACTIVE = 4;
295
296    class H extends Handler {
297        H(Looper looper) {
298            super(looper);
299        }
300
301        @Override
302        public void handleMessage(Message msg) {
303            switch (msg.what) {
304                case MSG_DUMP: {
305                    HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
306                    try {
307                        doDump((FileDescriptor)args.arg1,
308                                (PrintWriter)args.arg2, (String[])args.arg3);
309                    } catch (RuntimeException e) {
310                        ((PrintWriter)args.arg2).println("Exception: " + e);
311                    }
312                    synchronized (args.arg4) {
313                        ((CountDownLatch)args.arg4).countDown();
314                    }
315                    return;
316                }
317                case MSG_BIND: {
318                    final InputBindResult res = (InputBindResult)msg.obj;
319                    synchronized (mH) {
320                        if (mBindSequence < 0 || mBindSequence != res.sequence) {
321                            Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
322                                    + ", given seq=" + res.sequence);
323                            return;
324                        }
325
326                        mCurMethod = res.method;
327                        mCurId = res.id;
328                        mBindSequence = res.sequence;
329                    }
330                    startInputInner();
331                    return;
332                }
333                case MSG_UNBIND: {
334                    final int sequence = msg.arg1;
335                    synchronized (mH) {
336                        if (mBindSequence == sequence) {
337                            if (false) {
338                                // XXX the server has already unbound!
339                                if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
340                                    try {
341                                        mCurMethod.finishInput();
342                                    } catch (RemoteException e) {
343                                        Log.w(TAG, "IME died: " + mCurId, e);
344                                    }
345                                }
346                            }
347                            clearBindingLocked();
348
349                            // If we were actively using the last input method, then
350                            // we would like to re-connect to the next input method.
351                            if (mServedView != null && mServedView.isFocused()) {
352                                mServedConnecting = true;
353                            }
354                        }
355                        startInputInner();
356                    }
357                    return;
358                }
359                case MSG_SET_ACTIVE: {
360                    final boolean active = msg.arg1 != 0;
361                    synchronized (mH) {
362                        mActive = active;
363                        mFullscreenMode = false;
364                        if (!active) {
365                            // Some other client has starting using the IME, so note
366                            // that this happened and make sure our own editor's
367                            // state is reset.
368                            mHasBeenInactive = true;
369                            try {
370                                // Note that finishComposingText() is allowed to run
371                                // even when we are not active.
372                                mIInputContext.finishComposingText();
373                            } catch (RemoteException e) {
374                            }
375                        }
376                    }
377                    return;
378                }
379            }
380        }
381    }
382
383    class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
384        public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
385            super(mainLooper, conn);
386        }
387
388        public boolean isActive() {
389            return mActive;
390        }
391    }
392
393    final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
394        @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
395            // No need to check for dump permission, since we only give this
396            // interface to the system.
397
398            CountDownLatch latch = new CountDownLatch(1);
399            HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
400            sargs.arg1 = fd;
401            sargs.arg2 = fout;
402            sargs.arg3 = args;
403            sargs.arg4 = latch;
404            mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
405            try {
406                if (!latch.await(5, TimeUnit.SECONDS)) {
407                    fout.println("Timeout waiting for dump");
408                }
409            } catch (InterruptedException e) {
410                fout.println("Interrupted waiting for dump");
411            }
412        }
413
414        public void setUsingInputMethod(boolean state) {
415        }
416
417        public void onBindMethod(InputBindResult res) {
418            mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
419        }
420
421        public void onUnbindMethod(int sequence) {
422            mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
423        }
424
425        public void setActive(boolean active) {
426            mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
427        }
428    };
429
430    final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
431
432    InputMethodManager(IInputMethodManager service, Looper looper) {
433        mService = service;
434        mMainLooper = looper;
435        mH = new H(looper);
436        mIInputContext = new ControlledInputConnectionWrapper(looper,
437                mDummyInputConnection);
438
439        if (mInstance == null) {
440            mInstance = this;
441        }
442    }
443
444    /**
445     * Retrieve the global InputMethodManager instance, creating it if it
446     * doesn't already exist.
447     * @hide
448     */
449    static public InputMethodManager getInstance(Context context) {
450        return getInstance(context.getMainLooper());
451    }
452
453    /**
454     * Internally, the input method manager can't be context-dependent, so
455     * we have this here for the places that need it.
456     * @hide
457     */
458    static public InputMethodManager getInstance(Looper mainLooper) {
459        synchronized (mInstanceSync) {
460            if (mInstance != null) {
461                return mInstance;
462            }
463            IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
464            IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
465            mInstance = new InputMethodManager(service, mainLooper);
466        }
467        return mInstance;
468    }
469
470    /**
471     * Private optimization: retrieve the global InputMethodManager instance,
472     * if it exists.
473     * @hide
474     */
475    static public InputMethodManager peekInstance() {
476        return mInstance;
477    }
478
479    /** @hide */
480    public IInputMethodClient getClient() {
481        return mClient;
482    }
483
484    /** @hide */
485    public IInputContext getInputContext() {
486        return mIInputContext;
487    }
488
489    public List<InputMethodInfo> getInputMethodList() {
490        try {
491            return mService.getInputMethodList();
492        } catch (RemoteException e) {
493            throw new RuntimeException(e);
494        }
495    }
496
497    public List<InputMethodInfo> getEnabledInputMethodList() {
498        try {
499            return mService.getEnabledInputMethodList();
500        } catch (RemoteException e) {
501            throw new RuntimeException(e);
502        }
503    }
504
505    public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
506        try {
507            mService.updateStatusIcon(imeToken, packageName, iconId);
508        } catch (RemoteException e) {
509            throw new RuntimeException(e);
510        }
511    }
512
513    public void hideStatusIcon(IBinder imeToken) {
514        try {
515            mService.updateStatusIcon(imeToken, null, 0);
516        } catch (RemoteException e) {
517            throw new RuntimeException(e);
518        }
519    }
520
521    /** @hide */
522    public void setIMEButtonVisible(IBinder imeToken, boolean visible) {
523        try {
524            mService.setIMEButtonVisible(imeToken, visible);
525        } catch (RemoteException e) {
526            throw new RuntimeException(e);
527        }
528    }
529
530    /** @hide */
531    public void setFullscreenMode(boolean fullScreen) {
532        mFullscreenMode = fullScreen;
533    }
534
535    /**
536     * Allows you to discover whether the attached input method is running
537     * in fullscreen mode.  Return true if it is fullscreen, entirely covering
538     * your UI, else returns false.
539     */
540    public boolean isFullscreenMode() {
541        return mFullscreenMode;
542    }
543
544    /**
545     * Return true if the given view is the currently active view for the
546     * input method.
547     */
548    public boolean isActive(View view) {
549        checkFocus();
550        synchronized (mH) {
551            return (mServedView == view
552                    || (mServedView != null
553                            && mServedView.checkInputConnectionProxy(view)))
554                    && mCurrentTextBoxAttribute != null;
555        }
556    }
557
558    /**
559     * Return true if any view is currently active in the input method.
560     */
561    public boolean isActive() {
562        checkFocus();
563        synchronized (mH) {
564            return mServedView != null && mCurrentTextBoxAttribute != null;
565        }
566    }
567
568    /**
569     * Return true if the currently served view is accepting full text edits.
570     * If false, it has no input connection, so can only handle raw key events.
571     */
572    public boolean isAcceptingText() {
573        checkFocus();
574        return mServedInputConnection != null;
575    }
576
577    /**
578     * Reset all of the state associated with being bound to an input method.
579     */
580    void clearBindingLocked() {
581        clearConnectionLocked();
582        mBindSequence = -1;
583        mCurId = null;
584        mCurMethod = null;
585    }
586
587    /**
588     * Reset all of the state associated with a served view being connected
589     * to an input method
590     */
591    void clearConnectionLocked() {
592        mCurrentTextBoxAttribute = null;
593        mServedInputConnection = null;
594    }
595
596    /**
597     * Disconnect any existing input connection, clearing the served view.
598     */
599    void finishInputLocked() {
600        mNextServedView = null;
601        if (mServedView != null) {
602            if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
603
604            if (mCurrentTextBoxAttribute != null) {
605                try {
606                    mService.finishInput(mClient);
607                } catch (RemoteException e) {
608                }
609            }
610
611            if (mServedInputConnection != null) {
612                // We need to tell the previously served view that it is no
613                // longer the input target, so it can reset its state.  Schedule
614                // this call on its window's Handler so it will be on the correct
615                // thread and outside of our lock.
616                Handler vh = mServedView.getHandler();
617                if (vh != null) {
618                    // This will result in a call to reportFinishInputConnection()
619                    // below.
620                    vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
621                            mServedInputConnection));
622                }
623            }
624
625            mServedView = null;
626            mCompletions = null;
627            mServedConnecting = false;
628            clearConnectionLocked();
629        }
630    }
631
632    /**
633     * Called from the FINISH_INPUT_CONNECTION message above.
634     * @hide
635     */
636    public void reportFinishInputConnection(InputConnection ic) {
637        if (mServedInputConnection != ic) {
638            ic.finishComposingText();
639        }
640    }
641
642    public void displayCompletions(View view, CompletionInfo[] completions) {
643        checkFocus();
644        synchronized (mH) {
645            if (mServedView != view && (mServedView == null
646                            || !mServedView.checkInputConnectionProxy(view))) {
647                return;
648            }
649
650            mCompletions = completions;
651            if (mCurMethod != null) {
652                try {
653                    mCurMethod.displayCompletions(mCompletions);
654                } catch (RemoteException e) {
655                }
656            }
657        }
658    }
659
660    public void updateExtractedText(View view, int token, ExtractedText text) {
661        checkFocus();
662        synchronized (mH) {
663            if (mServedView != view && (mServedView == null
664                    || !mServedView.checkInputConnectionProxy(view))) {
665                return;
666            }
667
668            if (mCurMethod != null) {
669                try {
670                    mCurMethod.updateExtractedText(token, text);
671                } catch (RemoteException e) {
672                }
673            }
674        }
675    }
676
677    /**
678     * Flag for {@link #showSoftInput} to indicate that this is an implicit
679     * request to show the input window, not as the result of a direct request
680     * by the user.  The window may not be shown in this case.
681     */
682    public static final int SHOW_IMPLICIT = 0x0001;
683
684    /**
685     * Flag for {@link #showSoftInput} to indicate that the user has forced
686     * the input method open (such as by long-pressing menu) so it should
687     * not be closed until they explicitly do so.
688     */
689    public static final int SHOW_FORCED = 0x0002;
690
691    /**
692     * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
693     * a result receiver: explicitly request that the current input method's
694     * soft input area be shown to the user, if needed.
695     *
696     * @param view The currently focused view, which would like to receive
697     * soft keyboard input.
698     * @param flags Provides additional operating flags.  Currently may be
699     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
700     */
701    public boolean showSoftInput(View view, int flags) {
702        return showSoftInput(view, flags, null);
703    }
704
705    /**
706     * Flag for the {@link ResultReceiver} result code from
707     * {@link #showSoftInput(View, int, ResultReceiver)} and
708     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
709     * state of the soft input window was unchanged and remains shown.
710     */
711    public static final int RESULT_UNCHANGED_SHOWN = 0;
712
713    /**
714     * Flag for the {@link ResultReceiver} result code from
715     * {@link #showSoftInput(View, int, ResultReceiver)} and
716     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
717     * state of the soft input window was unchanged and remains hidden.
718     */
719    public static final int RESULT_UNCHANGED_HIDDEN = 1;
720
721    /**
722     * Flag for the {@link ResultReceiver} result code from
723     * {@link #showSoftInput(View, int, ResultReceiver)} and
724     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
725     * state of the soft input window changed from hidden to shown.
726     */
727    public static final int RESULT_SHOWN = 2;
728
729    /**
730     * Flag for the {@link ResultReceiver} result code from
731     * {@link #showSoftInput(View, int, ResultReceiver)} and
732     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the
733     * state of the soft input window changed from shown to hidden.
734     */
735    public static final int RESULT_HIDDEN = 3;
736
737    /**
738     * Explicitly request that the current input method's soft input area be
739     * shown to the user, if needed.  Call this if the user interacts with
740     * your view in such a way that they have expressed they would like to
741     * start performing input into it.
742     *
743     * @param view The currently focused view, which would like to receive
744     * soft keyboard input.
745     * @param flags Provides additional operating flags.  Currently may be
746     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
747     * @param resultReceiver If non-null, this will be called by the IME when
748     * it has processed your request to tell you what it has done.  The result
749     * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
750     * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
751     * {@link #RESULT_HIDDEN}.
752     */
753    public boolean showSoftInput(View view, int flags,
754            ResultReceiver resultReceiver) {
755        checkFocus();
756        synchronized (mH) {
757            if (mServedView != view && (mServedView == null
758                    || !mServedView.checkInputConnectionProxy(view))) {
759                return false;
760            }
761
762            try {
763                return mService.showSoftInput(mClient, flags, resultReceiver);
764            } catch (RemoteException e) {
765            }
766
767            return false;
768        }
769    }
770
771    /** @hide */
772    public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
773        try {
774            mService.showSoftInput(mClient, flags, resultReceiver);
775        } catch (RemoteException e) {
776        }
777    }
778
779    /**
780     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
781     * input window should only be hidden if it was not explicitly shown
782     * by the user.
783     */
784    public static final int HIDE_IMPLICIT_ONLY = 0x0001;
785
786    /**
787     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
788     * input window should normally be hidden, unless it was originally
789     * shown with {@link #SHOW_FORCED}.
790     */
791    public static final int HIDE_NOT_ALWAYS = 0x0002;
792
793    /**
794     * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)
795     * without a result: request to hide the soft input window from the
796     * context of the window that is currently accepting input.
797     *
798     * @param windowToken The token of the window that is making the request,
799     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
800     * @param flags Provides additional operating flags.  Currently may be
801     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
802     */
803    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
804        return hideSoftInputFromWindow(windowToken, flags, null);
805    }
806
807    /**
808     * Request to hide the soft input window from the context of the window
809     * that is currently accepting input.  This should be called as a result
810     * of the user doing some actually than fairly explicitly requests to
811     * have the input window hidden.
812     *
813     * @param windowToken The token of the window that is making the request,
814     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
815     * @param flags Provides additional operating flags.  Currently may be
816     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
817     * @param resultReceiver If non-null, this will be called by the IME when
818     * it has processed your request to tell you what it has done.  The result
819     * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
820     * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
821     * {@link #RESULT_HIDDEN}.
822     */
823    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
824            ResultReceiver resultReceiver) {
825        checkFocus();
826        synchronized (mH) {
827            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
828                return false;
829            }
830
831            try {
832                return mService.hideSoftInput(mClient, flags, resultReceiver);
833            } catch (RemoteException e) {
834            }
835            return false;
836        }
837    }
838
839
840    /**
841     * This method toggles the input method window display.
842     * If the input window is already displayed, it gets hidden.
843     * If not the input window will be displayed.
844     * @param windowToken The token of the window that is making the request,
845     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
846     * @param showFlags Provides additional operating flags.  May be
847     * 0 or have the {@link #SHOW_IMPLICIT},
848     * {@link #SHOW_FORCED} bit set.
849     * @param hideFlags Provides additional operating flags.  May be
850     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
851     * {@link #HIDE_NOT_ALWAYS} bit set.
852     **/
853    public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
854        synchronized (mH) {
855            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
856                return;
857            }
858            if (mCurMethod != null) {
859                try {
860                    mCurMethod.toggleSoftInput(showFlags, hideFlags);
861                } catch (RemoteException e) {
862                }
863            }
864        }
865    }
866
867    /*
868     * This method toggles the input method window display.
869     * If the input window is already displayed, it gets hidden.
870     * If not the input window will be displayed.
871     * @param showFlags Provides additional operating flags.  May be
872     * 0 or have the {@link #SHOW_IMPLICIT},
873     * {@link #SHOW_FORCED} bit set.
874     * @param hideFlags Provides additional operating flags.  May be
875     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
876     * {@link #HIDE_NOT_ALWAYS} bit set.
877     * @hide
878     */
879    public void toggleSoftInput(int showFlags, int hideFlags) {
880        if (mCurMethod != null) {
881            try {
882                mCurMethod.toggleSoftInput(showFlags, hideFlags);
883            } catch (RemoteException e) {
884            }
885        }
886    }
887
888    /**
889     * If the input method is currently connected to the given view,
890     * restart it with its new contents.  You should call this when the text
891     * within your view changes outside of the normal input method or key
892     * input flow, such as when an application calls TextView.setText().
893     *
894     * @param view The view whose text has changed.
895     */
896    public void restartInput(View view) {
897        checkFocus();
898        synchronized (mH) {
899            if (mServedView != view && (mServedView == null
900                    || !mServedView.checkInputConnectionProxy(view))) {
901                return;
902            }
903
904            mServedConnecting = true;
905        }
906
907        startInputInner();
908    }
909
910    void startInputInner() {
911        final View view;
912        synchronized (mH) {
913            view = mServedView;
914
915            // Make sure we have a window token for the served view.
916            if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
917            if (view == null) {
918                if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
919                return;
920            }
921        }
922
923        // Now we need to get an input connection from the served view.
924        // This is complicated in a couple ways: we can't be holding our lock
925        // when calling out to the view, and we need to make sure we call into
926        // the view on the same thread that is driving its view hierarchy.
927        Handler vh = view.getHandler();
928        if (vh == null) {
929            // If the view doesn't have a handler, something has changed out
930            // from under us, so just bail.
931            if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
932            return;
933        }
934        if (vh.getLooper() != Looper.myLooper()) {
935            // The view is running on a different thread than our own, so
936            // we need to reschedule our work for over there.
937            if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
938            vh.post(new Runnable() {
939                public void run() {
940                    startInputInner();
941                }
942            });
943            return;
944        }
945
946        // Okay we are now ready to call into the served view and have it
947        // do its stuff.
948        // Life is good: let's hook everything up!
949        EditorInfo tba = new EditorInfo();
950        tba.packageName = view.getContext().getPackageName();
951        tba.fieldId = view.getId();
952        InputConnection ic = view.onCreateInputConnection(tba);
953        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
954
955        synchronized (mH) {
956            // Now that we are locked again, validate that our state hasn't
957            // changed.
958            if (mServedView != view || !mServedConnecting) {
959                // Something else happened, so abort.
960                if (DEBUG) Log.v(TAG,
961                        "Starting input: finished by someone else (view="
962                        + mServedView + " conn=" + mServedConnecting + ")");
963                return;
964            }
965
966            // If we already have a text box, then this view is already
967            // connected so we want to restart it.
968            final boolean initial = mCurrentTextBoxAttribute == null;
969
970            // Hook 'em up and let 'er rip.
971            mCurrentTextBoxAttribute = tba;
972            mServedConnecting = false;
973            mServedInputConnection = ic;
974            IInputContext servedContext;
975            if (ic != null) {
976                mCursorSelStart = tba.initialSelStart;
977                mCursorSelEnd = tba.initialSelEnd;
978                mCursorCandStart = -1;
979                mCursorCandEnd = -1;
980                mCursorRect.setEmpty();
981                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
982            } else {
983                servedContext = null;
984            }
985
986            try {
987                if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
988                        + ic + " tba=" + tba + " initial=" + initial);
989                InputBindResult res = mService.startInput(mClient,
990                        servedContext, tba, initial, mCurMethod == null);
991                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
992                if (res != null) {
993                    if (res.id != null) {
994                        mBindSequence = res.sequence;
995                        mCurMethod = res.method;
996                    } else {
997                        // This means there is no input method available.
998                        if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
999                        return;
1000                    }
1001                }
1002                if (mCurMethod != null && mCompletions != null) {
1003                    try {
1004                        mCurMethod.displayCompletions(mCompletions);
1005                    } catch (RemoteException e) {
1006                    }
1007                }
1008            } catch (RemoteException e) {
1009                Log.w(TAG, "IME died: " + mCurId, e);
1010            }
1011        }
1012    }
1013
1014    /**
1015     * When the focused window is dismissed, this method is called to finish the
1016     * input method started before.
1017     * @hide
1018     */
1019    public void windowDismissed(IBinder appWindowToken) {
1020        checkFocus();
1021        synchronized (mH) {
1022            if (mServedView != null &&
1023                    mServedView.getWindowToken() == appWindowToken) {
1024                finishInputLocked();
1025            }
1026        }
1027    }
1028
1029    /**
1030     * Call this when a view receives focus.
1031     * @hide
1032     */
1033    public void focusIn(View view) {
1034        synchronized (mH) {
1035            focusInLocked(view);
1036        }
1037    }
1038
1039    void focusInLocked(View view) {
1040        if (DEBUG) Log.v(TAG, "focusIn: " + view);
1041
1042        if (mCurRootView != view.getRootView()) {
1043            // This is a request from a window that isn't in the window with
1044            // IME focus, so ignore it.
1045            if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
1046            return;
1047        }
1048
1049        mNextServedView = view;
1050        scheduleCheckFocusLocked(view);
1051    }
1052
1053    /**
1054     * Call this when a view loses focus.
1055     * @hide
1056     */
1057    public void focusOut(View view) {
1058        synchronized (mH) {
1059            if (DEBUG) Log.v(TAG, "focusOut: " + view
1060                    + " mServedView=" + mServedView
1061                    + " winFocus=" + view.hasWindowFocus());
1062            if (mServedView != view) {
1063                // The following code would auto-hide the IME if we end up
1064                // with no more views with focus.  This can happen, however,
1065                // whenever we go into touch mode, so it ends up hiding
1066                // at times when we don't really want it to.  For now it
1067                // seems better to just turn it all off.
1068                if (false && view.hasWindowFocus()) {
1069                    mNextServedView = null;
1070                    scheduleCheckFocusLocked(view);
1071                }
1072            }
1073        }
1074    }
1075
1076    void scheduleCheckFocusLocked(View view) {
1077        Handler vh = view.getHandler();
1078        if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) {
1079            // This will result in a call to checkFocus() below.
1080            vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS));
1081        }
1082    }
1083
1084    /**
1085     * @hide
1086     */
1087    public void checkFocus() {
1088        // This is called a lot, so short-circuit before locking.
1089        if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1090            return;
1091        }
1092
1093        InputConnection ic = null;
1094        synchronized (mH) {
1095            if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1096                return;
1097            }
1098            if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
1099                    + " next=" + mNextServedView
1100                    + " restart=" + mNextServedNeedsStart);
1101
1102            mNextServedNeedsStart = false;
1103            if (mNextServedView == null) {
1104                finishInputLocked();
1105                // In this case, we used to have a focused view on the window,
1106                // but no longer do.  We should make sure the input method is
1107                // no longer shown, since it serves no purpose.
1108                closeCurrentInput();
1109                return;
1110            }
1111
1112            ic = mServedInputConnection;
1113
1114            mServedView = mNextServedView;
1115            mCurrentTextBoxAttribute = null;
1116            mCompletions = null;
1117            mServedConnecting = true;
1118        }
1119
1120        if (ic != null) {
1121            ic.finishComposingText();
1122        }
1123
1124        startInputInner();
1125    }
1126
1127    void closeCurrentInput() {
1128        try {
1129            mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
1130        } catch (RemoteException e) {
1131        }
1132    }
1133
1134    /**
1135     * Called by ViewRoot when its window gets input focus.
1136     * @hide
1137     */
1138    public void onWindowFocus(View rootView, View focusedView, int softInputMode,
1139            boolean first, int windowFlags) {
1140        synchronized (mH) {
1141            if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
1142                    + " softInputMode=" + softInputMode
1143                    + " first=" + first + " flags=#"
1144                    + Integer.toHexString(windowFlags));
1145            if (mHasBeenInactive) {
1146                if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
1147                mHasBeenInactive = false;
1148                mNextServedNeedsStart = true;
1149            }
1150            focusInLocked(focusedView != null ? focusedView : rootView);
1151        }
1152
1153        checkFocus();
1154
1155        synchronized (mH) {
1156            try {
1157                final boolean isTextEditor = focusedView != null &&
1158                        focusedView.onCheckIsTextEditor();
1159                mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1160                        focusedView != null, isTextEditor, softInputMode, first,
1161                        windowFlags);
1162            } catch (RemoteException e) {
1163            }
1164        }
1165    }
1166
1167    /** @hide */
1168    public void startGettingWindowFocus(View rootView) {
1169        synchronized (mH) {
1170            mCurRootView = rootView;
1171        }
1172    }
1173
1174    /**
1175     * Report the current selection range.
1176     */
1177    public void updateSelection(View view, int selStart, int selEnd,
1178            int candidatesStart, int candidatesEnd) {
1179        checkFocus();
1180        synchronized (mH) {
1181            if ((mServedView != view && (mServedView == null
1182                        || !mServedView.checkInputConnectionProxy(view)))
1183                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1184                return;
1185            }
1186
1187            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
1188                    || mCursorCandStart != candidatesStart
1189                    || mCursorCandEnd != candidatesEnd) {
1190                if (DEBUG) Log.d(TAG, "updateSelection");
1191
1192                try {
1193                    if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
1194                    mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
1195                            selStart, selEnd, candidatesStart, candidatesEnd);
1196                    mCursorSelStart = selStart;
1197                    mCursorSelEnd = selEnd;
1198                    mCursorCandStart = candidatesStart;
1199                    mCursorCandEnd = candidatesEnd;
1200                } catch (RemoteException e) {
1201                    Log.w(TAG, "IME died: " + mCurId, e);
1202                }
1203            }
1204        }
1205    }
1206
1207    /**
1208     * Returns true if the current input method wants to watch the location
1209     * of the input editor's cursor in its window.
1210     */
1211    public boolean isWatchingCursor(View view) {
1212        return false;
1213    }
1214
1215    /**
1216     * Report the current cursor location in its window.
1217     */
1218    public void updateCursor(View view, int left, int top, int right, int bottom) {
1219        checkFocus();
1220        synchronized (mH) {
1221            if ((mServedView != view && (mServedView == null
1222                        || !mServedView.checkInputConnectionProxy(view)))
1223                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1224                return;
1225            }
1226
1227            mTmpCursorRect.set(left, top, right, bottom);
1228            if (!mCursorRect.equals(mTmpCursorRect)) {
1229                if (DEBUG) Log.d(TAG, "updateCursor");
1230
1231                try {
1232                    if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
1233                    mCurMethod.updateCursor(mTmpCursorRect);
1234                    mCursorRect.set(mTmpCursorRect);
1235                } catch (RemoteException e) {
1236                    Log.w(TAG, "IME died: " + mCurId, e);
1237                }
1238            }
1239        }
1240    }
1241
1242    /**
1243     * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
1244     * InputMethodSession.appPrivateCommand()} on the current Input Method.
1245     * @param view Optional View that is sending the command, or null if
1246     * you want to send the command regardless of the view that is attached
1247     * to the input method.
1248     * @param action Name of the command to be performed.  This <em>must</em>
1249     * be a scoped name, i.e. prefixed with a package name you own, so that
1250     * different developers will not create conflicting commands.
1251     * @param data Any data to include with the command.
1252     */
1253    public void sendAppPrivateCommand(View view, String action, Bundle data) {
1254        checkFocus();
1255        synchronized (mH) {
1256            if ((mServedView != view && (mServedView == null
1257                        || !mServedView.checkInputConnectionProxy(view)))
1258                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
1259                return;
1260            }
1261            try {
1262                if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
1263                mCurMethod.appPrivateCommand(action, data);
1264            } catch (RemoteException e) {
1265                Log.w(TAG, "IME died: " + mCurId, e);
1266            }
1267        }
1268    }
1269
1270    /**
1271     * Force switch to a new input method component.  This can only be called
1272     * from the currently active input method, as validated by the given token.
1273     * @param token Supplies the identifying token given to an input method
1274     * when it was started, which allows it to perform this operation on
1275     * itself.
1276     * @param id The unique identifier for the new input method to be switched to.
1277     */
1278    public void setInputMethod(IBinder token, String id) {
1279        try {
1280            mService.setInputMethod(token, id);
1281        } catch (RemoteException e) {
1282            throw new RuntimeException(e);
1283        }
1284    }
1285
1286    /**
1287     * Close/hide the input method's soft input area, so the user no longer
1288     * sees it or can interact with it.  This can only be called
1289     * from the currently active input method, as validated by the given token.
1290     *
1291     * @param token Supplies the identifying token given to an input method
1292     * when it was started, which allows it to perform this operation on
1293     * itself.
1294     * @param flags Provides additional operating flags.  Currently may be
1295     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
1296     * {@link #HIDE_NOT_ALWAYS} bit set.
1297     */
1298    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
1299        try {
1300            mService.hideMySoftInput(token, flags);
1301        } catch (RemoteException e) {
1302            throw new RuntimeException(e);
1303        }
1304    }
1305
1306    /**
1307     * Show the input method's soft input area, so the user
1308     * sees the input method window and can interact with it.
1309     * This can only be called from the currently active input method,
1310     * as validated by the given token.
1311     *
1312     * @param token Supplies the identifying token given to an input method
1313     * when it was started, which allows it to perform this operation on
1314     * itself.
1315     * @param flags Provides additional operating flags.  Currently may be
1316     * 0 or have the {@link #SHOW_IMPLICIT} or
1317     * {@link #SHOW_FORCED} bit set.
1318     */
1319    public void showSoftInputFromInputMethod(IBinder token, int flags) {
1320        try {
1321            mService.showMySoftInput(token, flags);
1322        } catch (RemoteException e) {
1323            throw new RuntimeException(e);
1324        }
1325    }
1326
1327    /**
1328     * @hide
1329     */
1330    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
1331            IInputMethodCallback callback) {
1332        synchronized (mH) {
1333            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
1334
1335            if (mCurMethod == null) {
1336                try {
1337                    callback.finishedEvent(seq, false);
1338                } catch (RemoteException e) {
1339                }
1340                return;
1341            }
1342
1343            if (key.getAction() == KeyEvent.ACTION_DOWN
1344                    && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
1345                showInputMethodPicker();
1346                try {
1347                    callback.finishedEvent(seq, true);
1348                } catch (RemoteException e) {
1349                }
1350                return;
1351            }
1352            try {
1353                if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
1354                mCurMethod.dispatchKeyEvent(seq, key, callback);
1355            } catch (RemoteException e) {
1356                Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
1357                try {
1358                    callback.finishedEvent(seq, false);
1359                } catch (RemoteException ex) {
1360                }
1361            }
1362        }
1363    }
1364
1365    /**
1366     * @hide
1367     */
1368    void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
1369            IInputMethodCallback callback) {
1370        synchronized (mH) {
1371            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
1372
1373            if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
1374                try {
1375                    callback.finishedEvent(seq, false);
1376                } catch (RemoteException e) {
1377                }
1378                return;
1379            }
1380
1381            try {
1382                if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
1383                mCurMethod.dispatchTrackballEvent(seq, motion, callback);
1384            } catch (RemoteException e) {
1385                Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
1386                try {
1387                    callback.finishedEvent(seq, false);
1388                } catch (RemoteException ex) {
1389                }
1390            }
1391        }
1392    }
1393
1394    public void showInputMethodPicker() {
1395        synchronized (mH) {
1396            try {
1397                mService.showInputMethodPickerFromClient(mClient);
1398            } catch (RemoteException e) {
1399                Log.w(TAG, "IME died: " + mCurId, e);
1400            }
1401        }
1402    }
1403
1404    public void showInputMethodSubtypePicker() {
1405        synchronized (mH) {
1406            try {
1407                mService.showInputMethodSubtypePickerFromClient(mClient);
1408            } catch (RemoteException e) {
1409                Log.w(TAG, "IME died: " + mCurId, e);
1410            }
1411        }
1412    }
1413
1414    public void showInputMethodAndSubtypeEnabler(String topId) {
1415        synchronized (mH) {
1416            try {
1417                mService.showInputMethodAndSubtypeEnablerFromClient(mClient, topId);
1418            } catch (RemoteException e) {
1419                Log.w(TAG, "IME died: " + mCurId, e);
1420            }
1421        }
1422    }
1423
1424    public InputMethodSubtype getCurrentInputMethodSubtype() {
1425        synchronized (mH) {
1426            try {
1427                return mService.getCurrentInputMethodSubtype();
1428            } catch (RemoteException e) {
1429                Log.w(TAG, "IME died: " + mCurId, e);
1430                return null;
1431            }
1432        }
1433    }
1434
1435    void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
1436        final Printer p = new PrintWriterPrinter(fout);
1437        p.println("Input method client state for " + this + ":");
1438
1439        p.println("  mService=" + mService);
1440        p.println("  mMainLooper=" + mMainLooper);
1441        p.println("  mIInputContext=" + mIInputContext);
1442        p.println("  mActive=" + mActive
1443                + " mHasBeenInactive=" + mHasBeenInactive
1444                + " mBindSequence=" + mBindSequence
1445                + " mCurId=" + mCurId);
1446        p.println("  mCurMethod=" + mCurMethod);
1447        p.println("  mCurRootView=" + mCurRootView);
1448        p.println("  mServedView=" + mServedView);
1449        p.println("  mNextServedNeedsStart=" + mNextServedNeedsStart
1450                + " mNextServedView=" + mNextServedView);
1451        p.println("  mServedConnecting=" + mServedConnecting);
1452        if (mCurrentTextBoxAttribute != null) {
1453            p.println("  mCurrentTextBoxAttribute:");
1454            mCurrentTextBoxAttribute.dump(p, "    ");
1455        } else {
1456            p.println("  mCurrentTextBoxAttribute: null");
1457        }
1458        p.println("  mServedInputConnection=" + mServedInputConnection);
1459        p.println("  mCompletions=" + mCompletions);
1460        p.println("  mCursorRect=" + mCursorRect);
1461        p.println("  mCursorSelStart=" + mCursorSelStart
1462                + " mCursorSelEnd=" + mCursorSelEnd
1463                + " mCursorCandStart=" + mCursorCandStart
1464                + " mCursorCandEnd=" + mCursorCandEnd);
1465    }
1466}
1467