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