Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.view;
     18 
     19 import com.android.internal.annotations.GuardedBy;
     20 
     21 import android.annotation.NonNull;
     22 import android.annotation.Nullable;
     23 import android.os.Bundle;
     24 import android.os.Handler;
     25 import android.os.Looper;
     26 import android.os.Message;
     27 import android.os.RemoteException;
     28 import android.util.Log;
     29 import android.view.KeyEvent;
     30 import android.view.inputmethod.CompletionInfo;
     31 import android.view.inputmethod.CorrectionInfo;
     32 import android.view.inputmethod.ExtractedTextRequest;
     33 import android.view.inputmethod.InputConnection;
     34 import android.view.inputmethod.InputConnectionInspector;
     35 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
     36 import android.view.inputmethod.InputContentInfo;
     37 
     38 public abstract class IInputConnectionWrapper extends IInputContext.Stub {
     39     static final String TAG = "IInputConnectionWrapper";
     40 
     41     private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
     42     private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
     43     private static final int DO_GET_SELECTED_TEXT = 25;
     44     private static final int DO_GET_CURSOR_CAPS_MODE = 30;
     45     private static final int DO_GET_EXTRACTED_TEXT = 40;
     46     private static final int DO_COMMIT_TEXT = 50;
     47     private static final int DO_COMMIT_COMPLETION = 55;
     48     private static final int DO_COMMIT_CORRECTION = 56;
     49     private static final int DO_SET_SELECTION = 57;
     50     private static final int DO_PERFORM_EDITOR_ACTION = 58;
     51     private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
     52     private static final int DO_SET_COMPOSING_TEXT = 60;
     53     private static final int DO_SET_COMPOSING_REGION = 63;
     54     private static final int DO_FINISH_COMPOSING_TEXT = 65;
     55     private static final int DO_SEND_KEY_EVENT = 70;
     56     private static final int DO_DELETE_SURROUNDING_TEXT = 80;
     57     private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81;
     58     private static final int DO_BEGIN_BATCH_EDIT = 90;
     59     private static final int DO_END_BATCH_EDIT = 95;
     60     private static final int DO_REPORT_FULLSCREEN_MODE = 100;
     61     private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
     62     private static final int DO_CLEAR_META_KEY_STATES = 130;
     63     private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
     64     private static final int DO_CLOSE_CONNECTION = 150;
     65     private static final int DO_COMMIT_CONTENT = 160;
     66 
     67     @GuardedBy("mLock")
     68     @Nullable
     69     private InputConnection mInputConnection;
     70 
     71     private Looper mMainLooper;
     72     private Handler mH;
     73     private Object mLock = new Object();
     74     @GuardedBy("mLock")
     75     private boolean mFinished = false;
     76     @GuardedBy("mLock")
     77     private String mInputMethodId;
     78 
     79     static class SomeArgs {
     80         Object arg1;
     81         Object arg2;
     82         IInputContextCallback callback;
     83         int seq;
     84     }
     85 
     86     class MyHandler extends Handler {
     87         MyHandler(Looper looper) {
     88             super(looper);
     89         }
     90 
     91         @Override
     92         public void handleMessage(Message msg) {
     93             executeMessage(msg);
     94         }
     95     }
     96 
     97     public IInputConnectionWrapper(Looper mainLooper, @NonNull InputConnection inputConnection) {
     98         mInputConnection = inputConnection;
     99         mMainLooper = mainLooper;
    100         mH = new MyHandler(mMainLooper);
    101     }
    102 
    103     @Nullable
    104     public InputConnection getInputConnection() {
    105         synchronized (mLock) {
    106             return mInputConnection;
    107         }
    108     }
    109 
    110     protected boolean isFinished() {
    111         synchronized (mLock) {
    112             return mFinished;
    113         }
    114     }
    115 
    116     public String getInputMethodId() {
    117         synchronized (mLock) {
    118             return mInputMethodId;
    119         }
    120     }
    121 
    122     public void setInputMethodId(final String inputMethodId) {
    123         synchronized (mLock) {
    124             mInputMethodId = inputMethodId;
    125         }
    126     }
    127 
    128     abstract protected boolean isActive();
    129 
    130     /**
    131      * Called when the user took some actions that should be taken into consideration to update the
    132      * LRU list for input method rotation.
    133      */
    134     abstract protected void onUserAction();
    135 
    136     /**
    137      * Called when the input method started or stopped full-screen mode.
    138      * @param enabled {@code true} if the input method starts full-screen mode.
    139      * @param calledInBackground {@code true} if this input connection is in a state when incoming
    140      * events are usually ignored.
    141      */
    142     abstract protected void onReportFullscreenMode(boolean enabled, boolean calledInBackground);
    143 
    144     public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
    145         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
    146     }
    147 
    148     public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) {
    149         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
    150     }
    151 
    152     public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
    153         dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
    154     }
    155 
    156     public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
    157         dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
    158     }
    159 
    160     public void getExtractedText(ExtractedTextRequest request,
    161             int flags, int seq, IInputContextCallback callback) {
    162         dispatchMessage(obtainMessageIOSC(DO_GET_EXTRACTED_TEXT, flags,
    163                 request, seq, callback));
    164     }
    165 
    166     public void commitText(CharSequence text, int newCursorPosition) {
    167         dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
    168     }
    169 
    170     public void commitCompletion(CompletionInfo text) {
    171         dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text));
    172     }
    173 
    174     public void commitCorrection(CorrectionInfo info) {
    175         dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info));
    176     }
    177 
    178     public void setSelection(int start, int end) {
    179         dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end));
    180     }
    181 
    182     public void performEditorAction(int id) {
    183         dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0));
    184     }
    185 
    186     public void performContextMenuAction(int id) {
    187         dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
    188     }
    189 
    190     public void setComposingRegion(int start, int end) {
    191         dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
    192     }
    193 
    194     public void setComposingText(CharSequence text, int newCursorPosition) {
    195         dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
    196     }
    197 
    198     public void finishComposingText() {
    199         dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
    200     }
    201 
    202     public void sendKeyEvent(KeyEvent event) {
    203         dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
    204     }
    205 
    206     public void clearMetaKeyStates(int states) {
    207         dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
    208     }
    209 
    210     public void deleteSurroundingText(int beforeLength, int afterLength) {
    211         dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
    212                 beforeLength, afterLength));
    213     }
    214 
    215     public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
    216         dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
    217                 beforeLength, afterLength));
    218     }
    219 
    220     public void beginBatchEdit() {
    221         dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
    222     }
    223 
    224     public void endBatchEdit() {
    225         dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
    226     }
    227 
    228     public void reportFullscreenMode(boolean enabled) {
    229         dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0));
    230     }
    231 
    232     public void performPrivateCommand(String action, Bundle data) {
    233         dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
    234     }
    235 
    236     public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
    237             IInputContextCallback callback) {
    238         dispatchMessage(obtainMessageISC(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
    239                 seq, callback));
    240     }
    241 
    242     public void closeConnection() {
    243         dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
    244     }
    245 
    246     public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
    247             int seq, IInputContextCallback callback) {
    248         dispatchMessage(obtainMessageIOOSC(DO_COMMIT_CONTENT, flags, inputContentInfo, opts, seq,
    249                 callback));
    250     }
    251 
    252     void dispatchMessage(Message msg) {
    253         // If we are calling this from the main thread, then we can call
    254         // right through.  Otherwise, we need to send the message to the
    255         // main thread.
    256         if (Looper.myLooper() == mMainLooper) {
    257             executeMessage(msg);
    258             msg.recycle();
    259             return;
    260         }
    261 
    262         mH.sendMessage(msg);
    263     }
    264 
    265     void executeMessage(Message msg) {
    266         switch (msg.what) {
    267             case DO_GET_TEXT_AFTER_CURSOR: {
    268                 SomeArgs args = (SomeArgs)msg.obj;
    269                 try {
    270                     InputConnection ic = getInputConnection();
    271                     if (ic == null || !isActive()) {
    272                         Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
    273                         args.callback.setTextAfterCursor(null, args.seq);
    274                         return;
    275                     }
    276                     args.callback.setTextAfterCursor(ic.getTextAfterCursor(
    277                             msg.arg1, msg.arg2), args.seq);
    278                 } catch (RemoteException e) {
    279                     Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e);
    280                 }
    281                 return;
    282             }
    283             case DO_GET_TEXT_BEFORE_CURSOR: {
    284                 SomeArgs args = (SomeArgs)msg.obj;
    285                 try {
    286                     InputConnection ic = getInputConnection();
    287                     if (ic == null || !isActive()) {
    288                         Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
    289                         args.callback.setTextBeforeCursor(null, args.seq);
    290                         return;
    291                     }
    292                     args.callback.setTextBeforeCursor(ic.getTextBeforeCursor(
    293                             msg.arg1, msg.arg2), args.seq);
    294                 } catch (RemoteException e) {
    295                     Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e);
    296                 }
    297                 return;
    298             }
    299             case DO_GET_SELECTED_TEXT: {
    300                 SomeArgs args = (SomeArgs)msg.obj;
    301                 try {
    302                     InputConnection ic = getInputConnection();
    303                     if (ic == null || !isActive()) {
    304                         Log.w(TAG, "getSelectedText on inactive InputConnection");
    305                         args.callback.setSelectedText(null, args.seq);
    306                         return;
    307                     }
    308                     args.callback.setSelectedText(ic.getSelectedText(
    309                             msg.arg1), args.seq);
    310                 } catch (RemoteException e) {
    311                     Log.w(TAG, "Got RemoteException calling setSelectedText", e);
    312                 }
    313                 return;
    314             }
    315             case DO_GET_CURSOR_CAPS_MODE: {
    316                 SomeArgs args = (SomeArgs)msg.obj;
    317                 try {
    318                     InputConnection ic = getInputConnection();
    319                     if (ic == null || !isActive()) {
    320                         Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
    321                         args.callback.setCursorCapsMode(0, args.seq);
    322                         return;
    323                     }
    324                     args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1),
    325                             args.seq);
    326                 } catch (RemoteException e) {
    327                     Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e);
    328                 }
    329                 return;
    330             }
    331             case DO_GET_EXTRACTED_TEXT: {
    332                 SomeArgs args = (SomeArgs)msg.obj;
    333                 try {
    334                     InputConnection ic = getInputConnection();
    335                     if (ic == null || !isActive()) {
    336                         Log.w(TAG, "getExtractedText on inactive InputConnection");
    337                         args.callback.setExtractedText(null, args.seq);
    338                         return;
    339                     }
    340                     args.callback.setExtractedText(ic.getExtractedText(
    341                             (ExtractedTextRequest)args.arg1, msg.arg1), args.seq);
    342                 } catch (RemoteException e) {
    343                     Log.w(TAG, "Got RemoteException calling setExtractedText", e);
    344                 }
    345                 return;
    346             }
    347             case DO_COMMIT_TEXT: {
    348                 InputConnection ic = getInputConnection();
    349                 if (ic == null || !isActive()) {
    350                     Log.w(TAG, "commitText on inactive InputConnection");
    351                     return;
    352                 }
    353                 ic.commitText((CharSequence)msg.obj, msg.arg1);
    354                 onUserAction();
    355                 return;
    356             }
    357             case DO_SET_SELECTION: {
    358                 InputConnection ic = getInputConnection();
    359                 if (ic == null || !isActive()) {
    360                     Log.w(TAG, "setSelection on inactive InputConnection");
    361                     return;
    362                 }
    363                 ic.setSelection(msg.arg1, msg.arg2);
    364                 return;
    365             }
    366             case DO_PERFORM_EDITOR_ACTION: {
    367                 InputConnection ic = getInputConnection();
    368                 if (ic == null || !isActive()) {
    369                     Log.w(TAG, "performEditorAction on inactive InputConnection");
    370                     return;
    371                 }
    372                 ic.performEditorAction(msg.arg1);
    373                 return;
    374             }
    375             case DO_PERFORM_CONTEXT_MENU_ACTION: {
    376                 InputConnection ic = getInputConnection();
    377                 if (ic == null || !isActive()) {
    378                     Log.w(TAG, "performContextMenuAction on inactive InputConnection");
    379                     return;
    380                 }
    381                 ic.performContextMenuAction(msg.arg1);
    382                 return;
    383             }
    384             case DO_COMMIT_COMPLETION: {
    385                 InputConnection ic = getInputConnection();
    386                 if (ic == null || !isActive()) {
    387                     Log.w(TAG, "commitCompletion on inactive InputConnection");
    388                     return;
    389                 }
    390                 ic.commitCompletion((CompletionInfo)msg.obj);
    391                 return;
    392             }
    393             case DO_COMMIT_CORRECTION: {
    394                 InputConnection ic = getInputConnection();
    395                 if (ic == null || !isActive()) {
    396                     Log.w(TAG, "commitCorrection on inactive InputConnection");
    397                     return;
    398                 }
    399                 ic.commitCorrection((CorrectionInfo)msg.obj);
    400                 return;
    401             }
    402             case DO_SET_COMPOSING_TEXT: {
    403                 InputConnection ic = getInputConnection();
    404                 if (ic == null || !isActive()) {
    405                     Log.w(TAG, "setComposingText on inactive InputConnection");
    406                     return;
    407                 }
    408                 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
    409                 onUserAction();
    410                 return;
    411             }
    412             case DO_SET_COMPOSING_REGION: {
    413                 InputConnection ic = getInputConnection();
    414                 if (ic == null || !isActive()) {
    415                     Log.w(TAG, "setComposingRegion on inactive InputConnection");
    416                     return;
    417                 }
    418                 ic.setComposingRegion(msg.arg1, msg.arg2);
    419                 return;
    420             }
    421             case DO_FINISH_COMPOSING_TEXT: {
    422                 InputConnection ic = getInputConnection();
    423                 // Note we do NOT check isActive() here, because this is safe
    424                 // for an IME to call at any time, and we need to allow it
    425                 // through to clean up our state after the IME has switched to
    426                 // another client.
    427                 if (ic == null) {
    428                     Log.w(TAG, "finishComposingText on inactive InputConnection");
    429                     return;
    430                 }
    431                 ic.finishComposingText();
    432                 return;
    433             }
    434             case DO_SEND_KEY_EVENT: {
    435                 InputConnection ic = getInputConnection();
    436                 if (ic == null || !isActive()) {
    437                     Log.w(TAG, "sendKeyEvent on inactive InputConnection");
    438                     return;
    439                 }
    440                 ic.sendKeyEvent((KeyEvent)msg.obj);
    441                 onUserAction();
    442                 return;
    443             }
    444             case DO_CLEAR_META_KEY_STATES: {
    445                 InputConnection ic = getInputConnection();
    446                 if (ic == null || !isActive()) {
    447                     Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
    448                     return;
    449                 }
    450                 ic.clearMetaKeyStates(msg.arg1);
    451                 return;
    452             }
    453             case DO_DELETE_SURROUNDING_TEXT: {
    454                 InputConnection ic = getInputConnection();
    455                 if (ic == null || !isActive()) {
    456                     Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
    457                     return;
    458                 }
    459                 ic.deleteSurroundingText(msg.arg1, msg.arg2);
    460                 return;
    461             }
    462             case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
    463                 InputConnection ic = getInputConnection();
    464                 if (ic == null || !isActive()) {
    465                     Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
    466                     return;
    467                 }
    468                 ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
    469                 return;
    470             }
    471             case DO_BEGIN_BATCH_EDIT: {
    472                 InputConnection ic = getInputConnection();
    473                 if (ic == null || !isActive()) {
    474                     Log.w(TAG, "beginBatchEdit on inactive InputConnection");
    475                     return;
    476                 }
    477                 ic.beginBatchEdit();
    478                 return;
    479             }
    480             case DO_END_BATCH_EDIT: {
    481                 InputConnection ic = getInputConnection();
    482                 if (ic == null || !isActive()) {
    483                     Log.w(TAG, "endBatchEdit on inactive InputConnection");
    484                     return;
    485                 }
    486                 ic.endBatchEdit();
    487                 return;
    488             }
    489             case DO_REPORT_FULLSCREEN_MODE: {
    490                 InputConnection ic = getInputConnection();
    491                 boolean isBackground = false;
    492                 if (ic == null || !isActive()) {
    493                     Log.w(TAG, "reportFullscreenMode on inexistent InputConnection");
    494                     isBackground = true;
    495                 }
    496                 final boolean enabled = msg.arg1 == 1;
    497                 if (!isBackground) {
    498                     ic.reportFullscreenMode(enabled);
    499                 }
    500                 // Due to the nature of asynchronous event handling, currently InputMethodService
    501                 // has relied on the fact that #reportFullscreenMode() can be handled even when the
    502                 // InputConnection is inactive.  We have to notify this event to InputMethodManager.
    503                 onReportFullscreenMode(enabled, isBackground);
    504                 return;
    505             }
    506             case DO_PERFORM_PRIVATE_COMMAND: {
    507                 InputConnection ic = getInputConnection();
    508                 if (ic == null || !isActive()) {
    509                     Log.w(TAG, "performPrivateCommand on inactive InputConnection");
    510                     return;
    511                 }
    512                 SomeArgs args = (SomeArgs)msg.obj;
    513                 ic.performPrivateCommand((String)args.arg1,
    514                         (Bundle)args.arg2);
    515                 return;
    516             }
    517             case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
    518                 SomeArgs args = (SomeArgs)msg.obj;
    519                 try {
    520                     InputConnection ic = getInputConnection();
    521                     if (ic == null || !isActive()) {
    522                         Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
    523                         args.callback.setRequestUpdateCursorAnchorInfoResult(false, args.seq);
    524                         return;
    525                     }
    526                     args.callback.setRequestUpdateCursorAnchorInfoResult(
    527                             ic.requestCursorUpdates(msg.arg1), args.seq);
    528                 } catch (RemoteException e) {
    529                     Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e);
    530                 }
    531                 return;
    532             }
    533             case DO_CLOSE_CONNECTION: {
    534                 // Note that we do not need to worry about race condition here, because 1) mFinished
    535                 // is updated only inside this block, and 2) the code here is running on a Handler
    536                 // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the
    537                 // same time.
    538                 if (isFinished()) {
    539                     return;
    540                 }
    541                 try {
    542                     InputConnection ic = getInputConnection();
    543                     // Note we do NOT check isActive() here, because this is safe
    544                     // for an IME to call at any time, and we need to allow it
    545                     // through to clean up our state after the IME has switched to
    546                     // another client.
    547                     if (ic == null) {
    548                         return;
    549                     }
    550                     @MissingMethodFlags
    551                     final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
    552                     if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
    553                         ic.closeConnection();
    554                     }
    555                 } finally {
    556                     synchronized (mLock) {
    557                         mInputConnection = null;
    558                         mFinished = true;
    559                     }
    560                 }
    561                 return;
    562             }
    563             case DO_COMMIT_CONTENT: {
    564                 final int flags = msg.arg1;
    565                 final boolean grantUriPermission =
    566                         (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0;
    567                 SomeArgs args = (SomeArgs) msg.obj;
    568                 try {
    569                     InputConnection ic = getInputConnection();
    570                     if (ic == null || !isActive()) {
    571                         Log.w(TAG, "commitContent on inactive InputConnection");
    572                         args.callback.setCommitContentResult(false, args.seq);
    573                         return;
    574                     }
    575                     final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
    576                     if (inputContentInfo == null || !inputContentInfo.validate()) {
    577                         Log.w(TAG, "commitContent with invalid inputContentInfo="
    578                                 + inputContentInfo);
    579                         args.callback.setCommitContentResult(false, args.seq);
    580                         return;
    581                     }
    582                     if (grantUriPermission) {
    583                         try {
    584                             inputContentInfo.requestPermission();
    585                         } catch (Exception e) {
    586                             Log.e(TAG, "InputConnectionInfo.requestPermission() failed", e);
    587                             args.callback.setCommitContentResult(false, args.seq);
    588                             return;
    589                         }
    590                     }
    591                     final boolean result =
    592                             ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
    593                     // If this request is not handled, then there is no reason to keep the URI
    594                     // permission.
    595                     if (grantUriPermission && !result) {
    596                         inputContentInfo.releasePermission();
    597                     }
    598                     args.callback.setCommitContentResult(result, args.seq);
    599                 } catch (RemoteException e) {
    600                     Log.w(TAG, "Got RemoteException calling commitContent", e);
    601                 }
    602                 return;
    603             }
    604         }
    605         Log.w(TAG, "Unhandled message code: " + msg.what);
    606     }
    607 
    608     Message obtainMessage(int what) {
    609         return mH.obtainMessage(what);
    610     }
    611 
    612     Message obtainMessageII(int what, int arg1, int arg2) {
    613         return mH.obtainMessage(what, arg1, arg2);
    614     }
    615 
    616     Message obtainMessageO(int what, Object arg1) {
    617         return mH.obtainMessage(what, 0, 0, arg1);
    618     }
    619 
    620     Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) {
    621         SomeArgs args = new SomeArgs();
    622         args.callback = callback;
    623         args.seq = seq;
    624         return mH.obtainMessage(what, arg1, 0, args);
    625     }
    626 
    627     Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) {
    628         SomeArgs args = new SomeArgs();
    629         args.callback = callback;
    630         args.seq = seq;
    631         return mH.obtainMessage(what, arg1, arg2, args);
    632     }
    633 
    634     Message obtainMessageIOOSC(int what, int arg1, Object objArg1, Object objArg2, int seq,
    635             IInputContextCallback callback) {
    636         SomeArgs args = new SomeArgs();
    637         args.arg1 = objArg1;
    638         args.arg2 = objArg2;
    639         args.callback = callback;
    640         args.seq = seq;
    641         return mH.obtainMessage(what, arg1, 0, args);
    642     }
    643 
    644     Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
    645             IInputContextCallback callback) {
    646         SomeArgs args = new SomeArgs();
    647         args.arg1 = arg2;
    648         args.callback = callback;
    649         args.seq = seq;
    650         return mH.obtainMessage(what, arg1, 0, args);
    651     }
    652 
    653     Message obtainMessageIO(int what, int arg1, Object arg2) {
    654         return mH.obtainMessage(what, arg1, 0, arg2);
    655     }
    656 
    657     Message obtainMessageOO(int what, Object arg1, Object arg2) {
    658         SomeArgs args = new SomeArgs();
    659         args.arg1 = arg1;
    660         args.arg2 = arg2;
    661         return mH.obtainMessage(what, 0, 0, args);
    662     }
    663 }
    664