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