• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.os.Build;
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.os.Trace;
29 import android.util.Log;
30 import android.util.imetracing.ImeTracing;
31 import android.util.imetracing.InputConnectionHelper;
32 import android.util.proto.ProtoOutputStream;
33 import android.view.KeyEvent;
34 import android.view.View;
35 import android.view.inputmethod.CompletionInfo;
36 import android.view.inputmethod.CorrectionInfo;
37 import android.view.inputmethod.DumpableInputConnection;
38 import android.view.inputmethod.ExtractedText;
39 import android.view.inputmethod.ExtractedTextRequest;
40 import android.view.inputmethod.InputConnection;
41 import android.view.inputmethod.InputConnectionInspector;
42 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
43 import android.view.inputmethod.InputContentInfo;
44 import android.view.inputmethod.InputMethodManager;
45 import android.view.inputmethod.SurroundingText;
46 
47 import com.android.internal.annotations.GuardedBy;
48 import com.android.internal.inputmethod.ICharSequenceResultCallback;
49 import com.android.internal.inputmethod.IExtractedTextResultCallback;
50 import com.android.internal.inputmethod.IIntResultCallback;
51 import com.android.internal.inputmethod.ISurroundingTextResultCallback;
52 import com.android.internal.os.SomeArgs;
53 
54 import java.lang.ref.WeakReference;
55 
56 public final class IInputConnectionWrapper extends IInputContext.Stub {
57     private static final String TAG = "IInputConnectionWrapper";
58     private static final boolean DEBUG = false;
59 
60     private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
61     private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
62     private static final int DO_GET_SELECTED_TEXT = 25;
63     private static final int DO_GET_CURSOR_CAPS_MODE = 30;
64     private static final int DO_GET_EXTRACTED_TEXT = 40;
65     private static final int DO_COMMIT_TEXT = 50;
66     private static final int DO_COMMIT_COMPLETION = 55;
67     private static final int DO_COMMIT_CORRECTION = 56;
68     private static final int DO_SET_SELECTION = 57;
69     private static final int DO_PERFORM_EDITOR_ACTION = 58;
70     private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
71     private static final int DO_SET_COMPOSING_TEXT = 60;
72     private static final int DO_SET_COMPOSING_REGION = 63;
73     private static final int DO_FINISH_COMPOSING_TEXT = 65;
74     private static final int DO_SEND_KEY_EVENT = 70;
75     private static final int DO_DELETE_SURROUNDING_TEXT = 80;
76     private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81;
77     private static final int DO_BEGIN_BATCH_EDIT = 90;
78     private static final int DO_END_BATCH_EDIT = 95;
79     private static final int DO_PERFORM_SPELL_CHECK = 110;
80     private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
81     private static final int DO_CLEAR_META_KEY_STATES = 130;
82     private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
83     private static final int DO_CLOSE_CONNECTION = 150;
84     private static final int DO_COMMIT_CONTENT = 160;
85     private static final int DO_GET_SURROUNDING_TEXT = 41;
86     private static final int DO_SET_IME_CONSUMES_INPUT = 170;
87 
88 
89     @GuardedBy("mLock")
90     @Nullable
91     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
92     private InputConnection mInputConnection;
93 
94     private Looper mMainLooper;
95     private Handler mH;
96     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
97     private final Object mLock = new Object();
98     @GuardedBy("mLock")
99     private boolean mFinished = false;
100 
101     private final InputMethodManager mParentInputMethodManager;
102     private final WeakReference<View> mServedView;
103 
104     class MyHandler extends Handler {
MyHandler(Looper looper)105         MyHandler(Looper looper) {
106             super(looper);
107         }
108 
109         @Override
handleMessage(Message msg)110         public void handleMessage(Message msg) {
111             executeMessage(msg);
112         }
113     }
114 
IInputConnectionWrapper(@onNull Looper mainLooper, @NonNull InputConnection inputConnection, @NonNull InputMethodManager inputMethodManager, @Nullable View servedView)115     public IInputConnectionWrapper(@NonNull Looper mainLooper,
116             @NonNull InputConnection inputConnection,
117             @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
118         mInputConnection = inputConnection;
119         mMainLooper = mainLooper;
120         mH = new MyHandler(mMainLooper);
121         mParentInputMethodManager = inputMethodManager;
122         mServedView = new WeakReference<>(servedView);
123     }
124 
125     @Nullable
getInputConnection()126     public InputConnection getInputConnection() {
127         synchronized (mLock) {
128             return mInputConnection;
129         }
130     }
131 
isFinished()132     private boolean isFinished() {
133         synchronized (mLock) {
134             return mFinished;
135         }
136     }
137 
isActive()138     public boolean isActive() {
139         return mParentInputMethodManager.isActive() && !isFinished();
140     }
141 
getServedView()142     public View getServedView() {
143         return mServedView.get();
144     }
145 
deactivate()146     public void deactivate() {
147         if (isFinished()) {
148             // This is a small performance optimization.  Still only the 1st call of
149             // reportFinish() will take effect.
150             return;
151         }
152         closeConnection();
153 
154         // Notify the app that the InputConnection was closed.
155         final View servedView = mServedView.get();
156         if (servedView != null) {
157             final Handler handler = servedView.getHandler();
158             // The handler is null if the view is already detached. When that's the case, for
159             // now, we simply don't dispatch this callback.
160             if (handler != null) {
161                 if (DEBUG) {
162                     Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
163                 }
164                 if (handler.getLooper().isCurrentThread()) {
165                     servedView.onInputConnectionClosedInternal();
166                 } else {
167                     handler.post(servedView::onInputConnectionClosedInternal);
168                 }
169             }
170         }
171     }
172 
173     @Override
toString()174     public String toString() {
175         return "IInputConnectionWrapper{"
176                 + "connection=" + getInputConnection()
177                 + " finished=" + isFinished()
178                 + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
179                 + " mServedView=" + mServedView.get()
180                 + "}";
181     }
182 
dumpDebug(ProtoOutputStream proto, long fieldId)183     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
184         synchronized (mLock) {
185             // Check that the call is initiated in the main thread of the current InputConnection
186             // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
187             // executed on this thread. Otherwise the messages are dispatched to the correct thread
188             // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
189             // reasons.
190             if ((mInputConnection instanceof DumpableInputConnection)
191                     && Looper.myLooper() == mMainLooper) {
192                 ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
193             }
194         }
195     }
196 
getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback)197     public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
198         dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback));
199     }
200 
getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback)201     public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) {
202         dispatchMessage(mH.obtainMessage(DO_GET_TEXT_BEFORE_CURSOR, length, flags, callback));
203     }
204 
getSelectedText(int flags, ICharSequenceResultCallback callback)205     public void getSelectedText(int flags, ICharSequenceResultCallback callback) {
206         dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
207     }
208 
209     /**
210      * Dispatches the request for retrieving surrounding text.
211      *
212      * <p>See {@link InputConnection#getSurroundingText(int, int, int)}.
213      */
getSurroundingText(int beforeLength, int afterLength, int flags, ISurroundingTextResultCallback callback)214     public void getSurroundingText(int beforeLength, int afterLength, int flags,
215             ISurroundingTextResultCallback callback) {
216         final SomeArgs args = SomeArgs.obtain();
217         args.arg1 = beforeLength;
218         args.arg2 = afterLength;
219         args.arg3 = flags;
220         args.arg4 = callback;
221         dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
222     }
223 
getCursorCapsMode(int reqModes, IIntResultCallback callback)224     public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
225         dispatchMessage(
226                 mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
227     }
228 
getExtractedText(ExtractedTextRequest request, int flags, IExtractedTextResultCallback callback)229     public void getExtractedText(ExtractedTextRequest request, int flags,
230             IExtractedTextResultCallback callback) {
231         final SomeArgs args = SomeArgs.obtain();
232         args.arg1 = request;
233         args.arg2 = callback;
234         dispatchMessage(mH.obtainMessage(DO_GET_EXTRACTED_TEXT, flags, 0 /* unused */, args));
235     }
236 
commitText(CharSequence text, int newCursorPosition)237     public void commitText(CharSequence text, int newCursorPosition) {
238         dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
239     }
240 
commitCompletion(CompletionInfo text)241     public void commitCompletion(CompletionInfo text) {
242         dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text));
243     }
244 
commitCorrection(CorrectionInfo info)245     public void commitCorrection(CorrectionInfo info) {
246         dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info));
247     }
248 
setSelection(int start, int end)249     public void setSelection(int start, int end) {
250         dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end));
251     }
252 
performEditorAction(int id)253     public void performEditorAction(int id) {
254         dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0));
255     }
256 
performContextMenuAction(int id)257     public void performContextMenuAction(int id) {
258         dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
259     }
260 
setComposingRegion(int start, int end)261     public void setComposingRegion(int start, int end) {
262         dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
263     }
264 
setComposingText(CharSequence text, int newCursorPosition)265     public void setComposingText(CharSequence text, int newCursorPosition) {
266         dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
267     }
268 
finishComposingText()269     public void finishComposingText() {
270         dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
271     }
272 
sendKeyEvent(KeyEvent event)273     public void sendKeyEvent(KeyEvent event) {
274         dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
275     }
276 
clearMetaKeyStates(int states)277     public void clearMetaKeyStates(int states) {
278         dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
279     }
280 
deleteSurroundingText(int beforeLength, int afterLength)281     public void deleteSurroundingText(int beforeLength, int afterLength) {
282         dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
283                 beforeLength, afterLength));
284     }
285 
deleteSurroundingTextInCodePoints(int beforeLength, int afterLength)286     public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
287         dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
288                 beforeLength, afterLength));
289     }
290 
beginBatchEdit()291     public void beginBatchEdit() {
292         dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
293     }
294 
endBatchEdit()295     public void endBatchEdit() {
296         dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
297     }
298 
299     /**
300      * Dispatches the request for performing spell check.
301      *
302      * @see InputConnection#performSpellCheck()
303      */
performSpellCheck()304     public void performSpellCheck() {
305         dispatchMessage(obtainMessage(DO_PERFORM_SPELL_CHECK));
306     }
307 
performPrivateCommand(String action, Bundle data)308     public void performPrivateCommand(String action, Bundle data) {
309         dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
310     }
311 
requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback)312     public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback) {
313         dispatchMessage(mH.obtainMessage(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
314                 0 /* unused */, callback));
315     }
316 
closeConnection()317     public void closeConnection() {
318         dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
319     }
320 
commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts, IIntResultCallback callback)321     public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
322             IIntResultCallback callback) {
323         final SomeArgs args = SomeArgs.obtain();
324         args.arg1 = inputContentInfo;
325         args.arg2 = opts;
326         args.arg3 = callback;
327         dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args));
328     }
329 
330     /**
331      * Dispatches the request for setting ime consumes input.
332      *
333      * <p>See {@link InputConnection#setImeConsumesInput(boolean)}.
334      */
setImeConsumesInput(boolean imeConsumesInput)335     public void setImeConsumesInput(boolean imeConsumesInput) {
336         dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput));
337     }
338 
dispatchMessage(Message msg)339     void dispatchMessage(Message msg) {
340         // If we are calling this from the main thread, then we can call
341         // right through.  Otherwise, we need to send the message to the
342         // main thread.
343         if (Looper.myLooper() == mMainLooper) {
344             executeMessage(msg);
345             msg.recycle();
346             return;
347         }
348 
349         mH.sendMessage(msg);
350     }
351 
executeMessage(Message msg)352     void executeMessage(Message msg) {
353         ProtoOutputStream icProto;
354         switch (msg.what) {
355             case DO_GET_TEXT_AFTER_CURSOR: {
356                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
357                 try {
358                     final ICharSequenceResultCallback callback =
359                             (ICharSequenceResultCallback) msg.obj;
360                     final InputConnection ic = getInputConnection();
361                     final CharSequence result;
362                     if (ic == null || !isActive()) {
363                         Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
364                         result = null;
365                     } else {
366                         result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
367                     }
368                     if (ImeTracing.getInstance().isEnabled()) {
369                         icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1,
370                                 msg.arg2, result);
371                         ImeTracing.getInstance().triggerClientDump(
372                                 TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
373                     }
374                     try {
375                         callback.onResult(result);
376                     } catch (RemoteException e) {
377                         Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
378                             + " result=" + result, e);
379                     }
380                 } finally {
381                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
382                 }
383                 return;
384             }
385             case DO_GET_TEXT_BEFORE_CURSOR: {
386                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
387                 try {
388                     final ICharSequenceResultCallback callback =
389                             (ICharSequenceResultCallback) msg.obj;
390                     final InputConnection ic = getInputConnection();
391                     final CharSequence result;
392                     if (ic == null || !isActive()) {
393                         Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
394                         result = null;
395                     } else {
396                         result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
397                     }
398                     if (ImeTracing.getInstance().isEnabled()) {
399                         icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1,
400                                 msg.arg2, result);
401                         ImeTracing.getInstance().triggerClientDump(
402                                 TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
403                     }
404                     try {
405                         callback.onResult(result);
406                     } catch (RemoteException e) {
407                         Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
408                             + " result=" + result, e);
409                     }
410                 } finally {
411                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
412                 }
413                 return;
414             }
415             case DO_GET_SELECTED_TEXT: {
416                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
417                 try {
418                     final ICharSequenceResultCallback callback =
419                             (ICharSequenceResultCallback) msg.obj;
420                     final InputConnection ic = getInputConnection();
421                     final CharSequence result;
422                     if (ic == null || !isActive()) {
423                         Log.w(TAG, "getSelectedText on inactive InputConnection");
424                         result = null;
425                     } else {
426                         result = ic.getSelectedText(msg.arg1);
427                     }
428                     if (ImeTracing.getInstance().isEnabled()) {
429                         icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result);
430                         ImeTracing.getInstance().triggerClientDump(
431                                 TAG + "#getSelectedText", mParentInputMethodManager, icProto);
432                     }
433                     try {
434                         callback.onResult(result);
435                     } catch (RemoteException e) {
436                         Log.w(TAG, "Failed to return the result to getSelectedText()."
437                                 + " result=" + result, e);
438                     }
439                 } finally {
440                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
441                 }
442                 return;
443             }
444             case DO_GET_SURROUNDING_TEXT: {
445                 final SomeArgs args = (SomeArgs) msg.obj;
446                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
447                 try {
448                     int beforeLength = (int) args.arg1;
449                     int afterLength  = (int) args.arg2;
450                     int flags = (int) args.arg3;
451                     final ISurroundingTextResultCallback callback =
452                             (ISurroundingTextResultCallback) args.arg4;
453                     final InputConnection ic = getInputConnection();
454                     final SurroundingText result;
455                     if (ic == null || !isActive()) {
456                         Log.w(TAG, "getSurroundingText on inactive InputConnection");
457                         result = null;
458                     } else {
459                         result = ic.getSurroundingText(beforeLength, afterLength, flags);
460                     }
461                     if (ImeTracing.getInstance().isEnabled()) {
462                         icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength,
463                                 afterLength, flags, result);
464                         ImeTracing.getInstance().triggerClientDump(
465                                 TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
466                     }
467                     try {
468                         callback.onResult(result);
469                     } catch (RemoteException e) {
470                         Log.w(TAG, "Failed to return the result to getSurroundingText()."
471                                 + " result=" + result, e);
472                     }
473                 } finally {
474                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
475                     args.recycle();
476                 }
477                 return;
478             }
479             case DO_GET_CURSOR_CAPS_MODE: {
480                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
481                 try {
482                     final IIntResultCallback callback = (IIntResultCallback) msg.obj;
483                     final InputConnection ic = getInputConnection();
484                     final int result;
485                     if (ic == null || !isActive()) {
486                         Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
487                         result = 0;
488                     } else {
489                         result = ic.getCursorCapsMode(msg.arg1);
490                     }
491                     if (ImeTracing.getInstance().isEnabled()) {
492                         icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1,
493                                 result);
494                         ImeTracing.getInstance().triggerClientDump(
495                                 TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
496                     }
497                     try {
498                         callback.onResult(result);
499                     } catch (RemoteException e) {
500                         Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
501                             + " result=" + result, e);
502                     }
503                 } finally {
504                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
505                 }
506                 return;
507             }
508             case DO_GET_EXTRACTED_TEXT: {
509                 final SomeArgs args = (SomeArgs) msg.obj;
510                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
511                 try {
512                     final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1;
513                     final IExtractedTextResultCallback callback =
514                             (IExtractedTextResultCallback) args.arg2;
515                     final InputConnection ic = getInputConnection();
516                     final ExtractedText result;
517                     if (ic == null || !isActive()) {
518                         Log.w(TAG, "getExtractedText on inactive InputConnection");
519                         result = null;
520                     } else {
521                         result = ic.getExtractedText(request, msg.arg1);
522                     }
523                     if (ImeTracing.getInstance().isEnabled()) {
524                         icProto = InputConnectionHelper.buildGetExtractedTextProto(request,
525                                 msg.arg1, result);
526                         ImeTracing.getInstance().triggerClientDump(
527                                 TAG + "#getExtractedText", mParentInputMethodManager, icProto);
528                     }
529                     try {
530                         callback.onResult(result);
531                     } catch (RemoteException e) {
532                         Log.w(TAG, "Failed to return the result to getExtractedText()."
533                                 + " result=" + result, e);
534                     }
535                 } finally {
536                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
537                     args.recycle();
538                 }
539                 return;
540             }
541             case DO_COMMIT_TEXT: {
542                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText");
543                 try {
544                     InputConnection ic = getInputConnection();
545                     if (ic == null || !isActive()) {
546                         Log.w(TAG, "commitText on inactive InputConnection");
547                         return;
548                     }
549                     ic.commitText((CharSequence) msg.obj, msg.arg1);
550                 } finally {
551                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
552                 }
553                 return;
554             }
555             case DO_SET_SELECTION: {
556                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection");
557                 try {
558                     InputConnection ic = getInputConnection();
559                     if (ic == null || !isActive()) {
560                         Log.w(TAG, "setSelection on inactive InputConnection");
561                         return;
562                     }
563                     ic.setSelection(msg.arg1, msg.arg2);
564                 } finally {
565                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
566                 }
567                 return;
568             }
569             case DO_PERFORM_EDITOR_ACTION: {
570                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction");
571                 try {
572                     InputConnection ic = getInputConnection();
573                     if (ic == null || !isActive()) {
574                         Log.w(TAG, "performEditorAction on inactive InputConnection");
575                         return;
576                     }
577                     ic.performEditorAction(msg.arg1);
578                 } finally {
579                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
580                 }
581                 return;
582             }
583             case DO_PERFORM_CONTEXT_MENU_ACTION: {
584                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction");
585                 try {
586                     InputConnection ic = getInputConnection();
587                     if (ic == null || !isActive()) {
588                         Log.w(TAG, "performContextMenuAction on inactive InputConnection");
589                         return;
590                     }
591                     ic.performContextMenuAction(msg.arg1);
592                 } finally {
593                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
594                 }
595                 return;
596             }
597             case DO_COMMIT_COMPLETION: {
598                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion");
599                 try {
600                     InputConnection ic = getInputConnection();
601                     if (ic == null || !isActive()) {
602                         Log.w(TAG, "commitCompletion on inactive InputConnection");
603                         return;
604                     }
605                     ic.commitCompletion((CompletionInfo) msg.obj);
606                 } finally {
607                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
608                 }
609                 return;
610             }
611             case DO_COMMIT_CORRECTION: {
612                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection");
613                 try {
614                     InputConnection ic = getInputConnection();
615                     if (ic == null || !isActive()) {
616                         Log.w(TAG, "commitCorrection on inactive InputConnection");
617                         return;
618                     }
619                     ic.commitCorrection((CorrectionInfo) msg.obj);
620                 } finally {
621                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
622                 }
623                 return;
624             }
625             case DO_SET_COMPOSING_TEXT: {
626                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText");
627                 try {
628                     InputConnection ic = getInputConnection();
629                     if (ic == null || !isActive()) {
630                         Log.w(TAG, "setComposingText on inactive InputConnection");
631                         return;
632                     }
633                     ic.setComposingText((CharSequence) msg.obj, msg.arg1);
634                 } finally {
635                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
636                 }
637                 return;
638             }
639             case DO_SET_COMPOSING_REGION: {
640                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion");
641                 try {
642                     InputConnection ic = getInputConnection();
643                     if (ic == null || !isActive()) {
644                         Log.w(TAG, "setComposingRegion on inactive InputConnection");
645                         return;
646                     }
647                     ic.setComposingRegion(msg.arg1, msg.arg2);
648                 } finally {
649                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
650                 }
651                 return;
652             }
653             case DO_FINISH_COMPOSING_TEXT: {
654                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText");
655                 try {
656                     if (isFinished()) {
657                         // In this case, #finishComposingText() is guaranteed to be called already.
658                         // There should be no negative impact if we ignore this call silently.
659                         if (DEBUG) {
660                             Log.w(TAG, "Bug 35301295: Redundant finishComposingText.");
661                         }
662                         return;
663                     }
664                     InputConnection ic = getInputConnection();
665                     // Note we do NOT check isActive() here, because this is safe
666                     // for an IME to call at any time, and we need to allow it
667                     // through to clean up our state after the IME has switched to
668                     // another client.
669                     if (ic == null) {
670                         Log.w(TAG, "finishComposingText on inactive InputConnection");
671                         return;
672                     }
673                     ic.finishComposingText();
674                 } finally {
675                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
676                 }
677                 return;
678             }
679             case DO_SEND_KEY_EVENT: {
680                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent");
681                 try {
682                     InputConnection ic = getInputConnection();
683                     if (ic == null || !isActive()) {
684                         Log.w(TAG, "sendKeyEvent on inactive InputConnection");
685                         return;
686                     }
687                     ic.sendKeyEvent((KeyEvent) msg.obj);
688                 } finally {
689                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
690                 }
691                 return;
692             }
693             case DO_CLEAR_META_KEY_STATES: {
694                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates");
695                 try {
696                     InputConnection ic = getInputConnection();
697                     if (ic == null || !isActive()) {
698                         Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
699                         return;
700                     }
701                     ic.clearMetaKeyStates(msg.arg1);
702                 } finally {
703                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
704                 }
705                 return;
706             }
707             case DO_DELETE_SURROUNDING_TEXT: {
708                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText");
709                 try {
710                     InputConnection ic = getInputConnection();
711                     if (ic == null || !isActive()) {
712                         Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
713                         return;
714                     }
715                     ic.deleteSurroundingText(msg.arg1, msg.arg2);
716                 } finally {
717                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
718                 }
719                 return;
720             }
721             case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
722                 Trace.traceBegin(Trace.TRACE_TAG_INPUT,
723                         "InputConnection#deleteSurroundingTextInCodePoints");
724                 try {
725                     InputConnection ic = getInputConnection();
726                     if (ic == null || !isActive()) {
727                         Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
728                         return;
729                     }
730                     ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
731                 } finally {
732                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
733                 }
734                 return;
735             }
736             case DO_BEGIN_BATCH_EDIT: {
737                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit");
738                 try {
739                     InputConnection ic = getInputConnection();
740                     if (ic == null || !isActive()) {
741                         Log.w(TAG, "beginBatchEdit on inactive InputConnection");
742                         return;
743                     }
744                     ic.beginBatchEdit();
745                 } finally {
746                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
747                 }
748                 return;
749             }
750             case DO_END_BATCH_EDIT: {
751                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit");
752                 try {
753                     InputConnection ic = getInputConnection();
754                     if (ic == null || !isActive()) {
755                         Log.w(TAG, "endBatchEdit on inactive InputConnection");
756                         return;
757                     }
758                     ic.endBatchEdit();
759                 } finally {
760                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
761                 }
762                 return;
763             }
764             case DO_PERFORM_SPELL_CHECK: {
765                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck");
766                 try {
767                     InputConnection ic = getInputConnection();
768                     if (ic == null || !isActive()) {
769                         Log.w(TAG, "performSpellCheck on inactive InputConnection");
770                         return;
771                     }
772                     ic.performSpellCheck();
773                 } finally {
774                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
775                 }
776                 return;
777             }
778             case DO_PERFORM_PRIVATE_COMMAND: {
779                 final SomeArgs args = (SomeArgs) msg.obj;
780                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand");
781                 try {
782                     final String action = (String) args.arg1;
783                     final Bundle data = (Bundle) args.arg2;
784                     InputConnection ic = getInputConnection();
785                     if (ic == null || !isActive()) {
786                         Log.w(TAG, "performPrivateCommand on inactive InputConnection");
787                         return;
788                     }
789                     ic.performPrivateCommand(action, data);
790                 } finally {
791                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
792                     args.recycle();
793                 }
794                 return;
795             }
796             case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
797                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
798                 try {
799                     final IIntResultCallback callback = (IIntResultCallback) msg.obj;
800                     final InputConnection ic = getInputConnection();
801                     final boolean result;
802                     if (ic == null || !isActive()) {
803                         Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
804                         result = false;
805                     } else {
806                         result = ic.requestCursorUpdates(msg.arg1);
807                     }
808                     try {
809                         callback.onResult(result ? 1 : 0);
810                     } catch (RemoteException e) {
811                         Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
812                                 + " result=" + result, e);
813                     }
814                 } finally {
815                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
816                 }
817                 return;
818             }
819             case DO_CLOSE_CONNECTION: {
820                 // Note that we do not need to worry about race condition here, because 1) mFinished
821                 // is updated only inside this block, and 2) the code here is running on a Handler
822                 // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the
823                 // same time.
824                 if (isFinished()) {
825                     return;
826                 }
827                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection");
828                 try {
829                     InputConnection ic = getInputConnection();
830                     // Note we do NOT check isActive() here, because this is safe
831                     // for an IME to call at any time, and we need to allow it
832                     // through to clean up our state after the IME has switched to
833                     // another client.
834                     if (ic == null) {
835                         return;
836                     }
837                     @MissingMethodFlags
838                     final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
839                     if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
840                         ic.closeConnection();
841                     }
842                 } finally {
843                     synchronized (mLock) {
844                         mInputConnection = null;
845                         mFinished = true;
846                     }
847                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
848                 }
849                 return;
850             }
851             case DO_COMMIT_CONTENT: {
852                 final int flags = msg.arg1;
853                 SomeArgs args = (SomeArgs) msg.obj;
854                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
855                 try {
856                     final IIntResultCallback callback = (IIntResultCallback) args.arg3;
857                     final InputConnection ic = getInputConnection();
858                     final boolean result;
859                     if (ic == null || !isActive()) {
860                         Log.w(TAG, "commitContent on inactive InputConnection");
861                         result = false;
862                     } else {
863                         final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
864                         if (inputContentInfo == null || !inputContentInfo.validate()) {
865                             Log.w(TAG, "commitContent with invalid inputContentInfo="
866                                     + inputContentInfo);
867                             result = false;
868                         } else {
869                             result = ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
870                         }
871                     }
872                     try {
873                         callback.onResult(result ? 1 : 0);
874                     } catch (RemoteException e) {
875                         Log.w(TAG, "Failed to return the result to commitContent()."
876                                 + " result=" + result, e);
877                     }
878                 } finally {
879                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
880                     args.recycle();
881                 }
882                 return;
883             }
884             case DO_SET_IME_CONSUMES_INPUT: {
885                 Trace.traceBegin(Trace.TRACE_TAG_INPUT,
886                         "InputConnection#setImeConsumesInput");
887                 try {
888                     InputConnection ic = getInputConnection();
889                     if (ic == null || !isActive()) {
890                         Log.w(TAG,
891                                 "setImeConsumesInput on inactive InputConnection");
892                         return;
893                     }
894                     ic.setImeConsumesInput(msg.arg1 == 1);
895                 } finally {
896                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
897                 }
898                 return;
899             }
900         }
901         Log.w(TAG, "Unhandled message code: " + msg.what);
902     }
903 
obtainMessage(int what)904     Message obtainMessage(int what) {
905         return mH.obtainMessage(what);
906     }
907 
obtainMessageII(int what, int arg1, int arg2)908     Message obtainMessageII(int what, int arg1, int arg2) {
909         return mH.obtainMessage(what, arg1, arg2);
910     }
911 
obtainMessageO(int what, Object arg1)912     Message obtainMessageO(int what, Object arg1) {
913         return mH.obtainMessage(what, 0, 0, arg1);
914     }
915 
obtainMessageIO(int what, int arg1, Object arg2)916     Message obtainMessageIO(int what, int arg1, Object arg2) {
917         return mH.obtainMessage(what, arg1, 0, arg2);
918     }
919 
obtainMessageOO(int what, Object arg1, Object arg2)920     Message obtainMessageOO(int what, Object arg1, Object arg2) {
921         final SomeArgs args = SomeArgs.obtain();
922         args.arg1 = arg1;
923         args.arg2 = arg2;
924         return mH.obtainMessage(what, 0, 0, args);
925     }
926 
obtainMessageB(int what, boolean arg1)927     Message obtainMessageB(int what, boolean arg1) {
928         return mH.obtainMessage(what, arg1 ? 1 : 0, 0);
929     }
930 }
931