Merge changes I6718aa31,I4dc6147f

* changes:
  Add InputConnection app side tracing
  Add InputConnection debug information
This commit is contained in:
Ioana Stefan
2020-11-30 11:33:14 +00:00
committed by Android (Google) Code Review
6 changed files with 396 additions and 151 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}
}
}

View File

@@ -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,61 +266,80 @@ 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;
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);
}
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
try {
callback.onResult(result);
} catch (RemoteException e) {
Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
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);
}
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: {
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);
}
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
try {
callback.onResult(result);
} catch (RemoteException e) {
Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
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);
}
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: {
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);
}
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
try {
callback.onResult(result);
} catch (RemoteException e) {
Log.w(TAG, "Failed to return the result to getSelectedText()."
+ " result=" + result, e);
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);
}
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;
@@ -335,30 +361,37 @@ 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: {
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);
}
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
try {
callback.onResult(result);
} catch (RemoteException e) {
Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
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);
}
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 =
@@ -378,159 +411,237 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
+ " result=" + result, e);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
args.recycle();
}
return;
}
case DO_COMMIT_TEXT: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitText on inactive InputConnection");
return;
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);
}
ic.commitText((CharSequence)msg.obj, msg.arg1);
return;
}
case DO_SET_SELECTION: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setSelection on inactive InputConnection");
return;
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);
}
ic.setSelection(msg.arg1, msg.arg2);
return;
}
case DO_PERFORM_EDITOR_ACTION: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "performEditorAction on inactive InputConnection");
return;
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);
}
ic.performEditorAction(msg.arg1);
return;
}
case DO_PERFORM_CONTEXT_MENU_ACTION: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "performContextMenuAction on inactive InputConnection");
return;
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);
}
ic.performContextMenuAction(msg.arg1);
return;
}
case DO_COMMIT_COMPLETION: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitCompletion on inactive InputConnection");
return;
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);
}
ic.commitCompletion((CompletionInfo)msg.obj);
return;
}
case DO_COMMIT_CORRECTION: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitCorrection on inactive InputConnection");
return;
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);
}
ic.commitCorrection((CorrectionInfo)msg.obj);
return;
}
case DO_SET_COMPOSING_TEXT: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setComposingText on inactive InputConnection");
return;
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);
}
ic.setComposingText((CharSequence)msg.obj, msg.arg1);
return;
}
case DO_SET_COMPOSING_REGION: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setComposingRegion on inactive InputConnection");
return;
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);
}
ic.setComposingRegion(msg.arg1, msg.arg2);
return;
}
case DO_FINISH_COMPOSING_TEXT: {
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.");
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;
}
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);
}
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();
return;
}
case DO_SEND_KEY_EVENT: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "sendKeyEvent on inactive InputConnection");
return;
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);
}
ic.sendKeyEvent((KeyEvent)msg.obj);
return;
}
case DO_CLEAR_META_KEY_STATES: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
return;
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);
}
ic.clearMetaKeyStates(msg.arg1);
return;
}
case DO_DELETE_SURROUNDING_TEXT: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
return;
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);
}
ic.deleteSurroundingText(msg.arg1, msg.arg2);
return;
}
case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
return;
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);
}
ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
return;
}
case DO_BEGIN_BATCH_EDIT: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "beginBatchEdit on inactive InputConnection");
return;
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);
}
ic.beginBatchEdit();
return;
}
case DO_END_BATCH_EDIT: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "endBatchEdit on inactive InputConnection");
return;
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);
}
ic.endBatchEdit();
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,25 +652,31 @@ 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: {
final IIntResultCallback callback = (IIntResultCallback) 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);
}
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
try {
callback.onResult(result ? 1 : 0);
} catch (RemoteException e) {
Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
+ " result=" + result, e);
final IIntResultCallback callback = (IIntResultCallback) 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 ? 1 : 0);
} catch (RemoteException e) {
Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
+ " result=" + result, e);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
return;
}
@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}