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