Loading core/java/android/view/inputmethod/DumpableInputConnection.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view.inputmethod; import android.annotation.NonNull; import android.util.proto.ProtoOutputStream; /** @hide */ public interface DumpableInputConnection { /** * Method used to dump state of InputConnection implementations of interest. * * @param proto Stream to write the state to * @param fieldId FieldId of DumpableInputConnection as defined in the parent message */ void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId); } core/java/android/view/inputmethod/InputMethodManager.java +19 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.util.imetracing.ImeTracing.PROTO_ARG; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL; import static android.view.inputmethod.InputMethodManagerProto.ACTIVE; Loading Loading @@ -998,9 +999,9 @@ public final class InputMethodManager { private final InputMethodManager mParentInputMethodManager; private final WeakReference<View> mServedView; ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn, ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn, InputMethodManager inputMethodManager, View servedView) { super(mainLooper, conn); super(icLooper, conn); mParentInputMethodManager = inputMethodManager; mServedView = new WeakReference<>(servedView); } Loading Loading @@ -1046,6 +1047,18 @@ public final class InputMethodManager { + " mServedView=" + mServedView.get() + "}"; } void dumpDebug(ProtoOutputStream proto, long fieldId) { // Check that the call is initiated in the main thread of the current InputConnection // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are // executed on this thread. Otherwise the messages are dispatched to the correct thread // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance // reasons. if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper() == getLooper()) { ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId); } } } private static class ImeThreadFactory implements ThreadFactory { Loading Loading @@ -2207,6 +2220,7 @@ public final class InputMethodManager { * @hide */ public void notifyImeHidden(IBinder windowToken) { ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this); synchronized (mH) { try { if (mCurMethod != null && mCurRootView != null Loading Loading @@ -3312,6 +3326,9 @@ public final class InputMethodManager { if (mImeInsetsConsumer != null) { mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER); } if (mServedInputConnectionWrapper != null) { mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION); } } } } core/java/com/android/internal/view/IInputConnectionWrapper.java +269 −148 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.Trace; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; Loading Loading @@ -111,6 +112,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } protected Looper getLooper() { synchronized (mMainLooper) { return mMainLooper; } } protected boolean isFinished() { synchronized (mLock) { return mFinished; Loading Loading @@ -259,7 +266,10 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { void executeMessage(Message msg) { switch (msg.what) { case DO_GET_TEXT_AFTER_CURSOR: { final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; 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()) { Loading @@ -274,10 +284,16 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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: { final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; 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()) { Loading @@ -292,10 +308,16 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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: { final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; 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()) { Loading @@ -310,10 +332,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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; Loading @@ -335,11 +361,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " 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; Loading @@ -355,10 +384,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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 = Loading @@ -378,83 +411,126 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " 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. Loading @@ -473,64 +549,99 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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_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; Loading @@ -541,11 +652,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } ic.performPrivateCommand(action, data); } finally { Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); try { final IIntResultCallback callback = (IIntResultCallback) msg.obj; final InputConnection ic = getInputConnection(); final boolean result; Loading @@ -561,6 +675,9 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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: { Loading @@ -571,6 +688,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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 Loading @@ -590,12 +708,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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 IIntResultCallback callback = (IIntResultCallback) args.arg3; final InputConnection ic = getInputConnection(); Loading @@ -620,6 +740,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; Loading core/java/com/android/internal/widget/EditableInputConnection.java +40 −1 Original line number Diff line number Diff line Loading @@ -16,21 +16,36 @@ package com.android.internal.widget; import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE; import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT; import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT; import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END; import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.text.Editable; import android.text.Selection; import android.text.method.KeyListener; import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.DumpableInputConnection; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.widget.TextView; public class EditableInputConnection extends BaseInputConnection { /** * Base class for an editable InputConnection instance. This is created by {@link TextView} or * {@link EditText}. */ public class EditableInputConnection extends BaseInputConnection implements DumpableInputConnection { private static final boolean DEBUG = false; private static final boolean DUMP_TEXT = false; private static final String TAG = "EditableInputConnection"; private final TextView mTextView; Loading Loading @@ -222,4 +237,28 @@ public class EditableInputConnection extends BaseInputConnection { } return true; } @Override public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); CharSequence editableText = mTextView.getText(); CharSequence selectedText = getSelectedText(0 /* flags */); if (DUMP_TEXT) { if (editableText != null) { proto.write(EDITABLE_TEXT, editableText.toString()); } if (selectedText != null) { proto.write(SELECTED_TEXT, selectedText.toString()); } } final Editable content = getEditable(); if (content != null) { int start = Selection.getSelectionStart(content); int end = Selection.getSelectionEnd(content); proto.write(SELECTED_TEXT_START, start); proto.write(SELECTED_TEXT_END, end); } proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0)); proto.end(token); } } core/proto/android/view/inputmethod/inputconnection.proto 0 → 100644 +34 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto2"; import "frameworks/base/core/proto/android/privacy.proto"; package android.view.inputmethod; option java_multiple_files = true; /** * Represents a {@link android.view.inputmethod.InputConnection} object. */ message InputConnectionProto { optional string editable_text = 1 [(.android.privacy).dest = DEST_LOCAL]; optional string selected_text = 2 [(.android.privacy).dest = DEST_LOCAL]; optional int32 selected_text_start = 3; optional int32 selected_text_end = 4; optional int32 cursor_caps_mode = 5; } No newline at end of file Loading
core/java/android/view/inputmethod/DumpableInputConnection.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view.inputmethod; import android.annotation.NonNull; import android.util.proto.ProtoOutputStream; /** @hide */ public interface DumpableInputConnection { /** * Method used to dump state of InputConnection implementations of interest. * * @param proto Stream to write the state to * @param fieldId FieldId of DumpableInputConnection as defined in the parent message */ void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId); }
core/java/android/view/inputmethod/InputMethodManager.java +19 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.util.imetracing.ImeTracing.PROTO_ARG; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL; import static android.view.inputmethod.InputMethodManagerProto.ACTIVE; Loading Loading @@ -998,9 +999,9 @@ public final class InputMethodManager { private final InputMethodManager mParentInputMethodManager; private final WeakReference<View> mServedView; ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn, ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn, InputMethodManager inputMethodManager, View servedView) { super(mainLooper, conn); super(icLooper, conn); mParentInputMethodManager = inputMethodManager; mServedView = new WeakReference<>(servedView); } Loading Loading @@ -1046,6 +1047,18 @@ public final class InputMethodManager { + " mServedView=" + mServedView.get() + "}"; } void dumpDebug(ProtoOutputStream proto, long fieldId) { // Check that the call is initiated in the main thread of the current InputConnection // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are // executed on this thread. Otherwise the messages are dispatched to the correct thread // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance // reasons. if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper() == getLooper()) { ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId); } } } private static class ImeThreadFactory implements ThreadFactory { Loading Loading @@ -2207,6 +2220,7 @@ public final class InputMethodManager { * @hide */ public void notifyImeHidden(IBinder windowToken) { ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this); synchronized (mH) { try { if (mCurMethod != null && mCurRootView != null Loading Loading @@ -3312,6 +3326,9 @@ public final class InputMethodManager { if (mImeInsetsConsumer != null) { mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER); } if (mServedInputConnectionWrapper != null) { mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION); } } } }
core/java/com/android/internal/view/IInputConnectionWrapper.java +269 −148 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.Trace; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; Loading Loading @@ -111,6 +112,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } protected Looper getLooper() { synchronized (mMainLooper) { return mMainLooper; } } protected boolean isFinished() { synchronized (mLock) { return mFinished; Loading Loading @@ -259,7 +266,10 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { void executeMessage(Message msg) { switch (msg.what) { case DO_GET_TEXT_AFTER_CURSOR: { final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; 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()) { Loading @@ -274,10 +284,16 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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: { final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; 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()) { Loading @@ -292,10 +308,16 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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: { final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; 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()) { Loading @@ -310,10 +332,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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; Loading @@ -335,11 +361,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " 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; Loading @@ -355,10 +384,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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 = Loading @@ -378,83 +411,126 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " 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. Loading @@ -473,64 +549,99 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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_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; Loading @@ -541,11 +652,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } ic.performPrivateCommand(action, data); } finally { Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); try { final IIntResultCallback callback = (IIntResultCallback) msg.obj; final InputConnection ic = getInputConnection(); final boolean result; Loading @@ -561,6 +675,9 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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: { Loading @@ -571,6 +688,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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 Loading @@ -590,12 +708,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { 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 IIntResultCallback callback = (IIntResultCallback) args.arg3; final InputConnection ic = getInputConnection(); Loading @@ -620,6 +740,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; Loading
core/java/com/android/internal/widget/EditableInputConnection.java +40 −1 Original line number Diff line number Diff line Loading @@ -16,21 +16,36 @@ package com.android.internal.widget; import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE; import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT; import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT; import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END; import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.text.Editable; import android.text.Selection; import android.text.method.KeyListener; import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.DumpableInputConnection; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.widget.TextView; public class EditableInputConnection extends BaseInputConnection { /** * Base class for an editable InputConnection instance. This is created by {@link TextView} or * {@link EditText}. */ public class EditableInputConnection extends BaseInputConnection implements DumpableInputConnection { private static final boolean DEBUG = false; private static final boolean DUMP_TEXT = false; private static final String TAG = "EditableInputConnection"; private final TextView mTextView; Loading Loading @@ -222,4 +237,28 @@ public class EditableInputConnection extends BaseInputConnection { } return true; } @Override public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); CharSequence editableText = mTextView.getText(); CharSequence selectedText = getSelectedText(0 /* flags */); if (DUMP_TEXT) { if (editableText != null) { proto.write(EDITABLE_TEXT, editableText.toString()); } if (selectedText != null) { proto.write(SELECTED_TEXT, selectedText.toString()); } } final Editable content = getEditable(); if (content != null) { int start = Selection.getSelectionStart(content); int end = Selection.getSelectionEnd(content); proto.write(SELECTED_TEXT_START, start); proto.write(SELECTED_TEXT_END, end); } proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0)); proto.end(token); } }
core/proto/android/view/inputmethod/inputconnection.proto 0 → 100644 +34 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto2"; import "frameworks/base/core/proto/android/privacy.proto"; package android.view.inputmethod; option java_multiple_files = true; /** * Represents a {@link android.view.inputmethod.InputConnection} object. */ message InputConnectionProto { optional string editable_text = 1 [(.android.privacy).dest = DEST_LOCAL]; optional string selected_text = 2 [(.android.privacy).dest = DEST_LOCAL]; optional int32 selected_text_start = 3; optional int32 selected_text_end = 4; optional int32 cursor_caps_mode = 5; } No newline at end of file