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