Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9393ea0 authored by Ioana Stefan's avatar Ioana Stefan Committed by Android (Google) Code Review
Browse files

Merge changes I6718aa31,I4dc6147f

* changes:
  Add InputConnection app side tracing
  Add InputConnection debug information
parents c6ba6568 bbdec456
Loading
Loading
Loading
Loading
+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);
}
+19 −2
Original line number Diff line number Diff line
@@ -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<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);
        }
@@ -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);
            }
        }
    }
}
+269 −148
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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()) {
@@ -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()) {
@@ -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()) {
@@ -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;
@@ -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;
@@ -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 =
@@ -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.
@@ -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;
@@ -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;
@@ -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: {
@@ -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
@@ -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();
@@ -620,6 +740,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
                                + " result=" + result, e);
                    }
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                    args.recycle();
                }
                return;
+40 −1
Original line number Diff line number Diff line
@@ -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);
    }
}
+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