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