Merge changes I6718aa31,I4dc6147f
* changes: Add InputConnection app side tracing Add InputConnection debug information
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
core/proto/android/view/inputmethod/inputconnection.proto
Normal file
34
core/proto/android/view/inputmethod/inputconnection.proto
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user