diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 46071296d0bf4..191bdf3cbbd73 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.Message; import android.os.RemoteException; import android.os.Trace; import android.util.Log; @@ -48,7 +47,6 @@ import com.android.internal.inputmethod.IIntResultCallback; import com.android.internal.inputmethod.ISurroundingTextResultCallback; import com.android.internal.inputmethod.ImeTracing; import com.android.internal.inputmethod.InputConnectionProtoDumper; -import com.android.internal.os.SomeArgs; import java.lang.ref.WeakReference; @@ -56,41 +54,12 @@ public final class IInputConnectionWrapper extends IInputContext.Stub { private static final String TAG = "IInputConnectionWrapper"; private static final boolean DEBUG = false; - private static final int DO_GET_TEXT_AFTER_CURSOR = 10; - private static final int DO_GET_TEXT_BEFORE_CURSOR = 20; - private static final int DO_GET_SELECTED_TEXT = 25; - private static final int DO_GET_CURSOR_CAPS_MODE = 30; - private static final int DO_GET_EXTRACTED_TEXT = 40; - private static final int DO_COMMIT_TEXT = 50; - private static final int DO_COMMIT_COMPLETION = 55; - private static final int DO_COMMIT_CORRECTION = 56; - private static final int DO_SET_SELECTION = 57; - private static final int DO_PERFORM_EDITOR_ACTION = 58; - private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59; - private static final int DO_SET_COMPOSING_TEXT = 60; - private static final int DO_SET_COMPOSING_REGION = 63; - private static final int DO_FINISH_COMPOSING_TEXT = 65; - private static final int DO_SEND_KEY_EVENT = 70; - private static final int DO_DELETE_SURROUNDING_TEXT = 80; - private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81; - private static final int DO_BEGIN_BATCH_EDIT = 90; - private static final int DO_END_BATCH_EDIT = 95; - private static final int DO_PERFORM_SPELL_CHECK = 110; - private static final int DO_PERFORM_PRIVATE_COMMAND = 120; - private static final int DO_CLEAR_META_KEY_STATES = 130; - private static final int DO_REQUEST_CURSOR_UPDATES = 140; - private static final int DO_CLOSE_CONNECTION = 150; - private static final int DO_COMMIT_CONTENT = 160; - private static final int DO_GET_SURROUNDING_TEXT = 41; - private static final int DO_SET_IME_CONSUMES_INPUT = 170; - - @GuardedBy("mLock") @Nullable private InputConnection mInputConnection; - private Looper mMainLooper; - private Handler mH; + private final Looper mMainLooper; + private final Handler mH; private final Object mLock = new Object(); @GuardedBy("mLock") @@ -99,23 +68,12 @@ public final class IInputConnectionWrapper extends IInputContext.Stub { private final InputMethodManager mParentInputMethodManager; private final WeakReference mServedView; - class MyHandler extends Handler { - MyHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - executeMessage(msg); - } - } - public IInputConnectionWrapper(@NonNull Looper mainLooper, @NonNull InputConnection inputConnection, @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) { mInputConnection = inputConnection; mMainLooper = mainLooper; - mH = new MyHandler(mMainLooper); + mH = new Handler(mMainLooper); mParentInputMethodManager = inputMethodManager; mServedView = new WeakReference<>(servedView); } @@ -193,15 +151,93 @@ public final class IInputConnectionWrapper extends IInputContext.Stub { } public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) { - dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); + try { + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextAfterCursor(length, flags); + } + if (ImeTracing.getInstance().isEnabled()) { + final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto( + length, flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextAfterCursor()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) { - dispatchMessage(mH.obtainMessage(DO_GET_TEXT_BEFORE_CURSOR, length, flags, callback)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor"); + try { + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextBeforeCursor(length, flags); + } + if (ImeTracing.getInstance().isEnabled()) { + final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto( + length, flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void getSelectedText(int flags, ICharSequenceResultCallback callback) { - dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText"); + try { + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getSelectedText on inactive InputConnection"); + result = null; + } else { + result = ic.getSelectedText(flags); + } + if (ImeTracing.getInstance().isEnabled()) { + final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto( + flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSelectedText", mParentInputMethodManager, icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getSelectedText()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } /** @@ -211,87 +247,347 @@ public final class IInputConnectionWrapper extends IInputContext.Stub { */ public void getSurroundingText(int beforeLength, int afterLength, int flags, ISurroundingTextResultCallback callback) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = beforeLength; - args.arg2 = afterLength; - args.arg3 = flags; - args.arg4 = callback; - dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText"); + try { + final InputConnection ic = getInputConnection(); + final SurroundingText result; + if (ic == null || !isActive()) { + Log.w(TAG, "getSurroundingText on inactive InputConnection"); + result = null; + } else { + result = ic.getSurroundingText(beforeLength, afterLength, flags); + } + if (ImeTracing.getInstance().isEnabled()) { + final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto( + beforeLength, afterLength, flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSurroundingText", mParentInputMethodManager, icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getSurroundingText()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void getCursorCapsMode(int reqModes, IIntResultCallback callback) { - dispatchMessage( - mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode"); + try { + final InputConnection ic = getInputConnection(); + final int result; + if (ic == null || !isActive()) { + Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); + result = 0; + } else { + result = ic.getCursorCapsMode(reqModes); + } + if (ImeTracing.getInstance().isEnabled()) { + final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto( + reqModes, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getCursorCapsMode()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void getExtractedText(ExtractedTextRequest request, int flags, IExtractedTextResultCallback callback) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = request; - args.arg2 = callback; - dispatchMessage(mH.obtainMessage(DO_GET_EXTRACTED_TEXT, flags, 0 /* unused */, args)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText"); + try { + final InputConnection ic = getInputConnection(); + final ExtractedText result; + if (ic == null || !isActive()) { + Log.w(TAG, "getExtractedText on inactive InputConnection"); + result = null; + } else { + result = ic.getExtractedText(request, flags); + } + if (ImeTracing.getInstance().isEnabled()) { + final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto( + request, flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getExtractedText", mParentInputMethodManager, icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getExtractedText()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void commitText(CharSequence text, int newCursorPosition) { - dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitText on inactive InputConnection"); + return; + } + ic.commitText(text, newCursorPosition); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void commitCompletion(CompletionInfo text) { - dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCompletion on inactive InputConnection"); + return; + } + ic.commitCompletion(text); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void commitCorrection(CorrectionInfo info) { - dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCorrection on inactive InputConnection"); + return; + } + ic.commitCorrection(info); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void setSelection(int start, int end) { - dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setSelection on inactive InputConnection"); + return; + } + ic.setSelection(start, end); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void performEditorAction(int id) { - dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performEditorAction on inactive InputConnection"); + return; + } + ic.performEditorAction(id); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } - + public void performContextMenuAction(int id) { - dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performContextMenuAction on inactive InputConnection"); + return; + } + ic.performContextMenuAction(id); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } - + public void setComposingRegion(int start, int end) { - dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingRegion on inactive InputConnection"); + return; + } + ic.setComposingRegion(start, end); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void setComposingText(CharSequence text, int newCursorPosition) { - dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingText on inactive InputConnection"); + return; + } + ic.setComposingText(text, newCursorPosition); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void finishComposingText() { - dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText"); + try { + if (isFinished()) { + // In this case, #finishComposingText() is guaranteed to be called already. + // There should be no negative impact if we ignore this call silently. + if (DEBUG) { + Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); + } + return; + } + InputConnection ic = getInputConnection(); + // Note we do NOT check isActive() here, because this is safe + // for an IME to call at any time, and we need to allow it + // through to clean up our state after the IME has switched to + // another client. + if (ic == null) { + Log.w(TAG, "finishComposingText on inactive InputConnection"); + return; + } + ic.finishComposingText(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void sendKeyEvent(KeyEvent event) { - dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "sendKeyEvent on inactive InputConnection"); + return; + } + ic.sendKeyEvent(event); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void clearMetaKeyStates(int states) { - dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); + return; + } + ic.clearMetaKeyStates(states); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void deleteSurroundingText(int beforeLength, int afterLength) { - dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT, - beforeLength, afterLength)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); + return; + } + ic.deleteSurroundingText(beforeLength, afterLength); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { - dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS, - beforeLength, afterLength)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, + "InputConnection#deleteSurroundingTextInCodePoints"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); + return; + } + ic.deleteSurroundingTextInCodePoints(beforeLength, afterLength); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void beginBatchEdit() { - dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "beginBatchEdit on inactive InputConnection"); + return; + } + ic.beginBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void endBatchEdit() { - dispatchMessage(obtainMessage(DO_END_BATCH_EDIT)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "endBatchEdit on inactive InputConnection"); + return; + } + ic.endBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } /** @@ -300,29 +596,123 @@ public final class IInputConnectionWrapper extends IInputContext.Stub { * @see InputConnection#performSpellCheck() */ public void performSpellCheck() { - dispatchMessage(obtainMessage(DO_PERFORM_SPELL_CHECK)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performSpellCheck on inactive InputConnection"); + return; + } + ic.performSpellCheck(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void performPrivateCommand(String action, Bundle data) { - dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performPrivateCommand on inactive InputConnection"); + return; + } + ic.performPrivateCommand(action, data); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback) { - dispatchMessage(mH.obtainMessage(DO_REQUEST_CURSOR_UPDATES, cursorUpdateMode, - 0 /* unused */, callback)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); + try { + final InputConnection ic = getInputConnection(); + final boolean result; + if (ic == null || !isActive()) { + Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); + result = false; + } else { + result = ic.requestCursorUpdates(cursorUpdateMode); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to requestCursorUpdates()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void closeConnection() { - dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION)); + dispatch(() -> { + // Note that we do not need to worry about race condition here, because 1) mFinished is + // updated only inside this block, and 2) the code here is running on a Handler hence we + // assume multiple closeConnection() tasks will not be handled at the same time. + if (isFinished()) { + return; + } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection"); + try { + InputConnection ic = getInputConnection(); + // Note we do NOT check isActive() here, because this is safe + // for an IME to call at any time, and we need to allow it + // through to clean up our state after the IME has switched to + // another client. + if (ic == null) { + return; + } + @MissingMethodFlags + final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic); + if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) { + ic.closeConnection(); + } + } finally { + synchronized (mLock) { + mInputConnection = null; + mFinished = true; + } + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts, IBooleanResultCallback callback) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = inputContentInfo; - args.arg2 = opts; - args.arg3 = callback; - dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent"); + try { + final InputConnection ic = getInputConnection(); + final boolean result; + if (ic == null || !isActive()) { + Log.w(TAG, "commitContent on inactive InputConnection"); + result = false; + } else { + if (inputContentInfo == null || !inputContentInfo.validate()) { + Log.w(TAG, "commitContent with invalid inputContentInfo=" + + inputContentInfo); + result = false; + } else { + result = ic.commitContent(inputContentInfo, flags, opts); + } + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to commitContent()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } /** @@ -331,599 +721,30 @@ public final class IInputConnectionWrapper extends IInputContext.Stub { *

See {@link InputConnection#setImeConsumesInput(boolean)}. */ public void setImeConsumesInput(boolean imeConsumesInput) { - dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput)); + dispatch(() -> { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setImeConsumesInput"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setImeConsumesInput on inactive InputConnection"); + return; + } + ic.setImeConsumesInput(imeConsumesInput); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + }); } - void dispatchMessage(Message msg) { + private void dispatch(@NonNull Runnable runnable) { // If we are calling this from the main thread, then we can call // right through. Otherwise, we need to send the message to the // main thread. - if (Looper.myLooper() == mMainLooper) { - executeMessage(msg); - msg.recycle(); + if (mMainLooper.isCurrentThread()) { + runnable.run(); return; } - - mH.sendMessage(msg); - } - void executeMessage(Message msg) { - byte[] icProto; - switch (msg.what) { - case DO_GET_TEXT_AFTER_CURSOR: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); - try { - final ICharSequenceResultCallback callback = - (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextAfterCursor(msg.arg1, msg.arg2); - } - if (ImeTracing.getInstance().isEnabled()) { - icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(msg.arg1, - msg.arg2, result); - ImeTracing.getInstance().triggerClientDump( - TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto); - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextAfterCursor()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_GET_TEXT_BEFORE_CURSOR: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor"); - try { - final ICharSequenceResultCallback callback = - (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); - } - if (ImeTracing.getInstance().isEnabled()) { - icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(msg.arg1, - msg.arg2, result); - ImeTracing.getInstance().triggerClientDump( - TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto); - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_GET_SELECTED_TEXT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText"); - try { - final ICharSequenceResultCallback callback = - (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getSelectedText on inactive InputConnection"); - result = null; - } else { - result = ic.getSelectedText(msg.arg1); - } - if (ImeTracing.getInstance().isEnabled()) { - icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(msg.arg1, - result); - ImeTracing.getInstance().triggerClientDump( - TAG + "#getSelectedText", mParentInputMethodManager, icProto); - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getSelectedText()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_GET_SURROUNDING_TEXT: { - final SomeArgs args = (SomeArgs) msg.obj; - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText"); - try { - int beforeLength = (int) args.arg1; - int afterLength = (int) args.arg2; - int flags = (int) args.arg3; - final ISurroundingTextResultCallback callback = - (ISurroundingTextResultCallback) args.arg4; - final InputConnection ic = getInputConnection(); - final SurroundingText result; - if (ic == null || !isActive()) { - Log.w(TAG, "getSurroundingText on inactive InputConnection"); - result = null; - } else { - result = ic.getSurroundingText(beforeLength, afterLength, flags); - } - if (ImeTracing.getInstance().isEnabled()) { - icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto( - beforeLength, afterLength, flags, result); - ImeTracing.getInstance().triggerClientDump( - TAG + "#getSurroundingText", mParentInputMethodManager, icProto); - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getSurroundingText()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - args.recycle(); - } - return; - } - case DO_GET_CURSOR_CAPS_MODE: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode"); - try { - final IIntResultCallback callback = (IIntResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final int result; - if (ic == null || !isActive()) { - Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); - result = 0; - } else { - result = ic.getCursorCapsMode(msg.arg1); - } - if (ImeTracing.getInstance().isEnabled()) { - icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(msg.arg1, - result); - ImeTracing.getInstance().triggerClientDump( - TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto); - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getCursorCapsMode()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_GET_EXTRACTED_TEXT: { - final SomeArgs args = (SomeArgs) msg.obj; - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText"); - try { - final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1; - final IExtractedTextResultCallback callback = - (IExtractedTextResultCallback) args.arg2; - final InputConnection ic = getInputConnection(); - final ExtractedText result; - if (ic == null || !isActive()) { - Log.w(TAG, "getExtractedText on inactive InputConnection"); - result = null; - } else { - result = ic.getExtractedText(request, msg.arg1); - } - if (ImeTracing.getInstance().isEnabled()) { - icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(request, - msg.arg1, result); - ImeTracing.getInstance().triggerClientDump( - TAG + "#getExtractedText", mParentInputMethodManager, icProto); - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getExtractedText()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - args.recycle(); - } - return; - } - case DO_COMMIT_TEXT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitText on inactive InputConnection"); - return; - } - ic.commitText((CharSequence) msg.obj, msg.arg1); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_SET_SELECTION: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setSelection on inactive InputConnection"); - return; - } - ic.setSelection(msg.arg1, msg.arg2); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_PERFORM_EDITOR_ACTION: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performEditorAction on inactive InputConnection"); - return; - } - ic.performEditorAction(msg.arg1); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_PERFORM_CONTEXT_MENU_ACTION: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performContextMenuAction on inactive InputConnection"); - return; - } - ic.performContextMenuAction(msg.arg1); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_COMMIT_COMPLETION: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCompletion on inactive InputConnection"); - return; - } - ic.commitCompletion((CompletionInfo) msg.obj); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_COMMIT_CORRECTION: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCorrection on inactive InputConnection"); - return; - } - ic.commitCorrection((CorrectionInfo) msg.obj); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_SET_COMPOSING_TEXT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingText on inactive InputConnection"); - return; - } - ic.setComposingText((CharSequence) msg.obj, msg.arg1); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_SET_COMPOSING_REGION: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingRegion on inactive InputConnection"); - return; - } - ic.setComposingRegion(msg.arg1, msg.arg2); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_FINISH_COMPOSING_TEXT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText"); - try { - if (isFinished()) { - // In this case, #finishComposingText() is guaranteed to be called already. - // There should be no negative impact if we ignore this call silently. - if (DEBUG) { - Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); - } - return; - } - InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. - if (ic == null) { - Log.w(TAG, "finishComposingText on inactive InputConnection"); - return; - } - ic.finishComposingText(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_SEND_KEY_EVENT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "sendKeyEvent on inactive InputConnection"); - return; - } - ic.sendKeyEvent((KeyEvent) msg.obj); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_CLEAR_META_KEY_STATES: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); - return; - } - ic.clearMetaKeyStates(msg.arg1); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_DELETE_SURROUNDING_TEXT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); - return; - } - ic.deleteSurroundingText(msg.arg1, msg.arg2); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, - "InputConnection#deleteSurroundingTextInCodePoints"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); - return; - } - ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_BEGIN_BATCH_EDIT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "beginBatchEdit on inactive InputConnection"); - return; - } - ic.beginBatchEdit(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_END_BATCH_EDIT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "endBatchEdit on inactive InputConnection"); - return; - } - ic.endBatchEdit(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_PERFORM_SPELL_CHECK: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performSpellCheck on inactive InputConnection"); - return; - } - ic.performSpellCheck(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_PERFORM_PRIVATE_COMMAND: { - final SomeArgs args = (SomeArgs) msg.obj; - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand"); - try { - final String action = (String) args.arg1; - final Bundle data = (Bundle) args.arg2; - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performPrivateCommand on inactive InputConnection"); - return; - } - ic.performPrivateCommand(action, data); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - args.recycle(); - } - return; - } - case DO_REQUEST_CURSOR_UPDATES: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); - try { - final IBooleanResultCallback callback = (IBooleanResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final boolean result; - if (ic == null || !isActive()) { - Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); - result = false; - } else { - result = ic.requestCursorUpdates(msg.arg1); - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to requestCursorUpdates()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_CLOSE_CONNECTION: { - // Note that we do not need to worry about race condition here, because 1) mFinished - // is updated only inside this block, and 2) the code here is running on a Handler - // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the - // same time. - if (isFinished()) { - return; - } - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection"); - try { - InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. - if (ic == null) { - return; - } - @MissingMethodFlags - final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic); - if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) { - ic.closeConnection(); - } - } finally { - synchronized (mLock) { - mInputConnection = null; - mFinished = true; - } - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - case DO_COMMIT_CONTENT: { - final int flags = msg.arg1; - SomeArgs args = (SomeArgs) msg.obj; - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent"); - try { - final IBooleanResultCallback callback = (IBooleanResultCallback) args.arg3; - final InputConnection ic = getInputConnection(); - final boolean result; - if (ic == null || !isActive()) { - Log.w(TAG, "commitContent on inactive InputConnection"); - result = false; - } else { - final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1; - if (inputContentInfo == null || !inputContentInfo.validate()) { - Log.w(TAG, "commitContent with invalid inputContentInfo=" - + inputContentInfo); - result = false; - } else { - result = ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2); - } - } - try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to commitContent()." - + " result=" + result, e); - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - args.recycle(); - } - return; - } - case DO_SET_IME_CONSUMES_INPUT: { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, - "InputConnection#setImeConsumesInput"); - try { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, - "setImeConsumesInput on inactive InputConnection"); - return; - } - ic.setImeConsumesInput(msg.arg1 == 1); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_INPUT); - } - return; - } - } - Log.w(TAG, "Unhandled message code: " + msg.what); - } - - Message obtainMessage(int what) { - return mH.obtainMessage(what); - } - - Message obtainMessageII(int what, int arg1, int arg2) { - return mH.obtainMessage(what, arg1, arg2); - } - - Message obtainMessageO(int what, Object arg1) { - return mH.obtainMessage(what, 0, 0, arg1); - } - - Message obtainMessageIO(int what, int arg1, Object arg2) { - return mH.obtainMessage(what, arg1, 0, arg2); - } - - Message obtainMessageOO(int what, Object arg1, Object arg2) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = arg1; - args.arg2 = arg2; - return mH.obtainMessage(what, 0, 0, args); - } - - Message obtainMessageB(int what, boolean arg1) { - return mH.obtainMessage(what, arg1 ? 1 : 0, 0); + mH.post(runnable); } }