diff --git a/core/java/android/view/inputmethod/DumpableInputConnection.java b/core/java/android/view/inputmethod/DumpableInputConnection.java new file mode 100644 index 0000000000000..9819a57344914 --- /dev/null +++ b/core/java/android/view/inputmethod/DumpableInputConnection.java @@ -0,0 +1,32 @@ +/* + * 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); +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 315d4461692cb..c3d3985b5ac38 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -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; @@ -998,9 +999,9 @@ public final class InputMethodManager { private final InputMethodManager mParentInputMethodManager; private final WeakReference 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); } @@ -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 { @@ -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 @@ -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); + } } } } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 33ebe43cb23ae..b299c7042d4e2 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -111,6 +111,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } + protected Looper getLooper() { + synchronized (mMainLooper) { + return mMainLooper; + } + } + protected boolean isFinished() { synchronized (mLock) { return mFinished; diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index ff3543c837ebc..d30d662806d49 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -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; @@ -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); + } } diff --git a/core/proto/android/view/inputmethod/inputconnection.proto b/core/proto/android/view/inputmethod/inputconnection.proto new file mode 100644 index 0000000000000..ad9a95aa95e6a --- /dev/null +++ b/core/proto/android/view/inputmethod/inputconnection.proto @@ -0,0 +1,34 @@ +/* + * 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 diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto index 5c0f341cb9e46..c1dce6f2d093b 100644 --- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -24,6 +24,7 @@ import "frameworks/base/core/proto/android/view/viewrootimpl.proto"; import "frameworks/base/core/proto/android/view/insetscontroller.proto"; import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto"; import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto"; import "frameworks/base/core/proto/android/view/imefocuscontroller.proto"; import "frameworks/base/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto"; @@ -70,6 +71,7 @@ message InputMethodClientsTraceProto { optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5; optional EditorInfoProto editor_info = 6; optional ImeFocusControllerProto ime_focus_controller = 7; + optional InputConnectionProto input_connection = 8; } }