diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 7cf0b10031acc..3cd13a212a4b8 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -199,10 +199,11 @@ public abstract class AbstractInputMethodService extends Service * Dumps the internal state of IME to a protocol buffer output stream. * * @param proto ProtoOutputStream to dump data to. + * @param icProto {@link InputConnection} call data in proto format. * @hide */ @SuppressWarnings("HiddenAbstractMethod") - public abstract void dumpProtoInternal(ProtoOutputStream proto); + public abstract void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto); /** * Implement this to handle {@link android.os.Binder#dump Binder.dump()} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 67e75d205f977..5576857d1f6b9 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -25,6 +25,7 @@ import static android.inputmethodservice.InputMethodServiceProto.EXTRACTED_TOKEN import static android.inputmethodservice.InputMethodServiceProto.EXTRACT_VIEW_HIDDEN; import static android.inputmethodservice.InputMethodServiceProto.FULLSCREEN_APPLIED; import static android.inputmethodservice.InputMethodServiceProto.INPUT_BINDING; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_CONNECTION_CALL; import static android.inputmethodservice.InputMethodServiceProto.INPUT_EDITOR_INFO; import static android.inputmethodservice.InputMethodServiceProto.INPUT_STARTED; import static android.inputmethodservice.InputMethodServiceProto.INPUT_VIEW_STARTED; @@ -742,7 +743,8 @@ public class InputMethodService extends AbstractInputMethodService { return; } ImeTracing.getInstance().triggerServiceDump( - "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this); + "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this, + null /* icProto */); final boolean wasVisible = isInputViewShown(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput"); @@ -798,7 +800,8 @@ public class InputMethodService extends AbstractInputMethodService { } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput"); ImeTracing.getInstance().triggerServiceDump( - "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this); + "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this, + null /* icProto */); final boolean wasVisible = isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { @@ -2182,7 +2185,8 @@ public class InputMethodService extends AbstractInputMethodService { return; } - ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this, + null /* icProto */); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); mDecorViewWasVisible = mDecorViewVisible; mInShowWindow = true; @@ -2260,7 +2264,8 @@ public class InputMethodService extends AbstractInputMethodService { */ private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) { ImeTracing.getInstance().triggerServiceDump( - "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this); + "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this, + null /* icProto */); mPrivOps.applyImeVisibility(setVisible ? mCurShowInputToken : mCurHideInputToken, setVisible); } @@ -2285,7 +2290,8 @@ public class InputMethodService extends AbstractInputMethodService { public void hideWindow() { if (DEBUG) Log.v(TAG, "CALL: hideWindow"); - ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this, + null /* icProto */); mWindowVisible = false; finishViews(false /* finishingInput */); if (mDecorViewVisible) { @@ -2356,7 +2362,8 @@ public class InputMethodService extends AbstractInputMethodService { void doFinishInput() { if (DEBUG) Log.v(TAG, "CALL: doFinishInput"); - ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this, + null /* icProto */); finishViews(true /* finishingInput */); if (mInputStarted) { mInlineSuggestionSessionController.notifyOnFinishInput(); @@ -2372,7 +2379,8 @@ public class InputMethodService extends AbstractInputMethodService { if (!restarting && mInputStarted) { doFinishInput(); } - ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this, + null /* icProto */); mInputStarted = true; mStartedInputConnection = ic; mInputEditorInfo = attribute; @@ -2531,7 +2539,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public void requestHideSelf(int flags) { - ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this, + null /* icProto */); mPrivOps.hideMySoftInput(flags); } @@ -2544,7 +2553,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public final void requestShowSelf(int flags) { - ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this, + null /* icProto */); mPrivOps.showMySoftInput(flags); } @@ -3364,7 +3374,7 @@ public class InputMethodService extends AbstractInputMethodService { * @hide */ @Override - public final void dumpProtoInternal(ProtoOutputStream proto) { + public final void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto) { final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE); mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW); proto.write(VIEWS_CREATED, mViewsCreated); @@ -3393,6 +3403,9 @@ public class InputMethodService extends AbstractInputMethodService { proto.write(STATUS_ICON, mStatusIcon); mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS); proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver)); + if (icProto != null) { + proto.write(INPUT_CONNECTION_CALL, icProto.getBytes()); + } proto.end(token); } } diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java index 4058eef3e2a37..723f1dd15e238 100644 --- a/core/java/android/util/imetracing/ImeTracing.java +++ b/core/java/android/util/imetracing/ImeTracing.java @@ -110,15 +110,20 @@ public abstract class ImeTracing { * * @param where Place where the trace was triggered. * @param immInstance The {@link InputMethodManager} instance to dump. + * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. */ - public abstract void triggerClientDump(String where, InputMethodManager immInstance); + public abstract void triggerClientDump(String where, InputMethodManager immInstance, + ProtoOutputStream icProto); /** * Starts a proto dump of the currently connected InputMethodService information. * * @param where Place where the trace was triggered. + * @param service The {@link android.inputmethodservice.InputMethodService} to be dumped. + * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. */ - public abstract void triggerServiceDump(String where, AbstractInputMethodService service); + public abstract void triggerServiceDump(String where, AbstractInputMethodService service, + ProtoOutputStream icProto); /** * Starts a proto dump of the InputMethodManagerService information. diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java index 904b44da97d74..6cc652d942ccb 100644 --- a/core/java/android/util/imetracing/ImeTracingClientImpl.java +++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java @@ -45,7 +45,8 @@ class ImeTracingClientImpl extends ImeTracing { } @Override - public void triggerClientDump(String where, @NonNull InputMethodManager immInstance) { + public void triggerClientDump(String where, @NonNull InputMethodManager immInstance, + ProtoOutputStream icProto) { if (!isEnabled() || !isAvailable()) { return; } @@ -59,7 +60,7 @@ class ImeTracingClientImpl extends ImeTracing { try { ProtoOutputStream proto = new ProtoOutputStream(); - immInstance.dumpDebug(proto); + immInstance.dumpDebug(proto, icProto); sendToService(proto.getBytes(), IME_TRACING_FROM_CLIENT, where); } catch (RemoteException e) { Log.e(TAG, "Exception while sending ime-related client dump to server", e); @@ -69,7 +70,8 @@ class ImeTracingClientImpl extends ImeTracing { } @Override - public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service) { + public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service, + ProtoOutputStream icProto) { if (!isEnabled() || !isAvailable()) { return; } @@ -83,7 +85,7 @@ class ImeTracingClientImpl extends ImeTracing { try { ProtoOutputStream proto = new ProtoOutputStream(); - service.dumpProtoInternal(proto); + service.dumpProtoInternal(proto, icProto); sendToService(proto.getBytes(), IME_TRACING_FROM_IMS, where); } catch (RemoteException e) { Log.e(TAG, "Exception while sending ime-related service dump to server", e); diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java index d758d77fb2f26..e793c280afbc8 100644 --- a/core/java/android/util/imetracing/ImeTracingServerImpl.java +++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java @@ -133,12 +133,14 @@ class ImeTracingServerImpl extends ImeTracing { } @Override - public void triggerClientDump(String where, InputMethodManager immInstance) { + public void triggerClientDump(String where, InputMethodManager immInstance, + ProtoOutputStream icProto) { // Intentionally left empty, this is implemented in ImeTracingClientImpl } @Override - public void triggerServiceDump(String where, AbstractInputMethodService service) { + public void triggerServiceDump(String where, AbstractInputMethodService service, + ProtoOutputStream icProto) { // Intentionally left empty, this is implemented in ImeTracingClientImpl } diff --git a/core/java/android/util/imetracing/InputConnectionHelper.java b/core/java/android/util/imetracing/InputConnectionHelper.java new file mode 100644 index 0000000000000..39f1e01eb4a9f --- /dev/null +++ b/core/java/android/util/imetracing/InputConnectionHelper.java @@ -0,0 +1,231 @@ +/* + * 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.util.imetracing; + +import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE; +import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_SELECTED_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_SURROUNDING_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_AFTER_CURSOR; +import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_BEFORE_CURSOR; +import static android.view.inputmethod.InputConnectionCallProto.GetExtractedText.REQUEST; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.proto.ProtoOutputStream; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnectionCallProto.GetCursorCapsMode; +import android.view.inputmethod.InputConnectionCallProto.GetExtractedText; +import android.view.inputmethod.InputConnectionCallProto.GetSelectedText; +import android.view.inputmethod.InputConnectionCallProto.GetSurroundingText; +import android.view.inputmethod.InputConnectionCallProto.GetTextAfterCursor; +import android.view.inputmethod.InputConnectionCallProto.GetTextBeforeCursor; +import android.view.inputmethod.SurroundingText; + +/** + * Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are + * integrated into {@link ImeTracing}. + * @hide + */ +public class InputConnectionHelper { + static final String TAG = "InputConnectionHelper"; + public static final boolean DUMP_TEXT = false; + + private InputConnectionHelper() {} + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getTextAfterCursor(int, int)} data. + * + * @param length The expected length of the text. This must be non-negative. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result The text after the cursor position; the length of the + * returned text might be less than length. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetTextAfterCursorProto(@IntRange(from = 0) int length, + int flags, @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_TEXT_AFTER_CURSOR); + proto.write(GetTextAfterCursor.LENGTH, length); + proto.write(GetTextAfterCursor.FLAGS, flags); + if (result == null) { + proto.write(GetTextAfterCursor.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetTextAfterCursor.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getTextBeforeCursor(int, int)} data. + * + * @param length The expected length of the text. This must be non-negative. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result The text before the cursor position; the length of the + * returned text might be less than length. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetTextBeforeCursorProto(@IntRange(from = 0) int length, + int flags, @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_TEXT_BEFORE_CURSOR); + proto.write(GetTextBeforeCursor.LENGTH, length); + proto.write(GetTextBeforeCursor.FLAGS, flags); + if (result == null) { + proto.write(GetTextBeforeCursor.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetTextBeforeCursor.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getSelectedText(int)} data. + * + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result the text that is currently selected, if any, or null if + * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and + * later, returns false when the target application does not implement + * this method. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetSelectedTextProto(int flags, + @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_SELECTED_TEXT); + proto.write(GetSelectedText.FLAGS, flags); + if (result == null) { + proto.write(GetSelectedText.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetSelectedText.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getSurroundingText(int, int, int)} data. + * + * @param beforeLength The expected length of the text before the cursor. + * @param afterLength The expected length of the text after the cursor. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result an {@link android.view.inputmethod.SurroundingText} object describing the + * surrounding text and state of selection, or null if the input connection is no longer valid, + * or the editor can't comply with the request for some reason, or the application does not + * implement this method. The length of the returned text might be less than the sum of + * beforeLength and afterLength . + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetSurroundingTextProto(@IntRange(from = 0) + int beforeLength, @IntRange(from = 0) int afterLength, int flags, + @Nullable SurroundingText result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_SURROUNDING_TEXT); + proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength); + proto.write(GetSurroundingText.AFTER_LENGTH, afterLength); + proto.write(GetSurroundingText.FLAGS, flags); + if (result == null) { + final long token_result = proto.start(GetSurroundingText.RESULT); + proto.write(GetSurroundingText.SurroundingText.TEXT, "null result"); + proto.end(token_result); + } else if (DUMP_TEXT) { + final long token_result = proto.start(GetSurroundingText.RESULT); + proto.write(GetSurroundingText.SurroundingText.TEXT, result.getText().toString()); + proto.write(GetSurroundingText.SurroundingText.SELECTION_START, + result.getSelectionStart()); + proto.write(GetSurroundingText.SurroundingText.SELECTION_END, + result.getSelectionEnd()); + proto.write(GetSurroundingText.SurroundingText.OFFSET, result.getOffset()); + proto.end(token_result); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getCursorCapsMode(int)} data. + * + * @param reqModes The desired modes to retrieve, as defined by + * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}. + * @param result the caps mode flags that are in effect at the current + * cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetCursorCapsModeProto(int reqModes, int result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_CURSOR_CAPS_MODE); + proto.write(GetCursorCapsMode.REQ_MODES, reqModes); + if (DUMP_TEXT) { + proto.write(GetCursorCapsMode.RESULT, result); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getExtractedText(ExtractedTextRequest, int)} + * data. + * + * @param request Description of how the text should be returned. + * {@link android.view.inputmethod.ExtractedTextRequest} + * @param flags Additional options to control the client, either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}. + * @param result an {@link android.view.inputmethod.ExtractedText} + * object describing the state of the text view and containing the + * extracted text itself, or null if the input connection is no + * longer valid of the editor can't comply with the request for + * some reason. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetExtractedTextProto(@NonNull ExtractedTextRequest + request, int flags, @Nullable ExtractedText result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_EXTRACTED_TEXT); + final long token_request = proto.start(REQUEST); + proto.write(GetExtractedText.ExtractedTextRequest.TOKEN, request.token); + proto.write(GetExtractedText.ExtractedTextRequest.FLAGS, request.flags); + proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_LINES, request.hintMaxLines); + proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_CHARS, request.hintMaxChars); + proto.end(token_request); + proto.write(GetExtractedText.FLAGS, flags); + if (result == null) { + proto.write(GetExtractedText.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetExtractedText.RESULT, result.text.toString()); + } + proto.end(token); + return proto; + } +} diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 1c82619a61ade..c4f32c433598c 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -829,7 +829,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#show", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0); } else { @@ -886,7 +886,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation void hide(@InsetsType int types, boolean fromIme) { if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#hide", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0); } else { Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); @@ -928,7 +928,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (fromIme) { ImeTracing.getInstance().triggerClientDump( "InsetsController#controlWindowInsetsAnimation", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, @@ -1022,7 +1022,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation animationType, mHost.getTranslator()); if ((typesReady & WindowInsets.Type.ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsAnimationControlImpl", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } mRunningAnimations.add(new RunningAnimation(runner, animationType)); if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: " @@ -1200,7 +1200,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (types.valueAt(j) == ITYPE_IME) { ImeTracing.getInstance().triggerClientDump( "InsetsSourceConsumer#notifyAnimationFinished", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } @@ -1345,7 +1345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean fromIme) { if ((types & ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsController#hideDirectly", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } final ArraySet internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { @@ -1361,7 +1361,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void showDirectly(@InsetsType int types, boolean fromIme) { if ((types & ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsController#showDirectly", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } final ArraySet internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2bea0d6b4b04c..8321f2b28771e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -9225,7 +9225,8 @@ public final class ViewRootImpl implements ViewParent, final ViewRootImpl viewAncestor = mViewAncestor.get(); if (fromIme) { ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#showInsets", - viewAncestor.getInsetsController().getHost().getInputMethodManager()); + viewAncestor.getInsetsController().getHost().getInputMethodManager(), + null /* icProto */); } if (viewAncestor != null) { viewAncestor.showInsets(types, fromIme); @@ -9238,7 +9239,8 @@ public final class ViewRootImpl implements ViewParent, final ViewRootImpl viewAncestor = mViewAncestor.get(); if (fromIme) { ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#hideInsets", - viewAncestor.getInsetsController().getHost().getInputMethodManager()); + viewAncestor.getInsetsController().getHost().getInputMethodManager(), + null /* icProto */); } if (viewAncestor != null) { viewAncestor.hideInsets(types, fromIme); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 8d2c2d96637fd..907b5b085b590 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -23,6 +23,7 @@ import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodCl 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_CONNECTION_CALL; 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; @@ -579,7 +580,8 @@ public final class InputMethodManager { int windowFlags) { final View servedView; ImeTracing.getInstance().triggerClientDump( - "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this); + "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this, + null /* icProto */); synchronized (mH) { mCurrentTextBoxAttribute = null; mCompletions = null; @@ -1016,6 +1018,11 @@ public final class InputMethodManager { return mParentInputMethodManager.mActive && !isFinished(); } + @Override + public InputMethodManager getIMM() { + return mParentInputMethodManager; + } + void deactivate() { if (isFinished()) { // This is a small performance optimization. Still only the 1st call of @@ -1713,7 +1720,8 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. */ public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { - ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this); + ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this, + null /* icProto */); // Re-dispatch if there is a context mismatch. final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); if (fallbackImm != null) { @@ -1822,7 +1830,7 @@ public final class InputMethodManager { public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver) { ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow", - this); + this, null /* icProto */); checkFocus(); synchronized (mH) { final View servedView = getServedViewLocked(); @@ -2228,7 +2236,8 @@ public final class InputMethodManager { * @hide */ public void notifyImeHidden(IBinder windowToken) { - ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this); + ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this, + null /* icProto */); synchronized (mH) { try { if (mCurMethod != null && mCurRootView != null @@ -3296,7 +3305,7 @@ public final class InputMethodManager { for (String arg : args) { if (arg.equals(PROTO_ARG)) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - dumpDebug(proto); + dumpDebug(proto, null /* icProto */); proto.flush(); return true; } @@ -3309,10 +3318,11 @@ public final class InputMethodManager { * {@link ProtoOutputStream}. * * @param proto The proto stream to which the dumps are written. + * @param icProto {@link InputConnection} call data in proto format. * @hide */ @GuardedBy("mH") - public void dumpDebug(ProtoOutputStream proto) { + public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) { if (mCurMethod == null) { return; } @@ -3337,6 +3347,9 @@ public final class InputMethodManager { if (mServedInputConnectionWrapper != null) { mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION); } + if (icProto != null) { + proto.write(INPUT_CONNECTION_CALL, icProto.getBytes()); + } } } } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 4deb40a0d772e..1d5935db63225 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -27,6 +27,9 @@ import android.os.Message; import android.os.RemoteException; import android.os.Trace; import android.util.Log; +import android.util.imetracing.ImeTracing; +import android.util.imetracing.InputConnectionHelper; +import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -36,6 +39,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionInspector; import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; import android.view.inputmethod.InputContentInfo; +import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.SurroundingText; import com.android.internal.annotations.GuardedBy; @@ -124,7 +128,9 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } - abstract protected boolean isActive(); + protected abstract boolean isActive(); + + protected abstract InputMethodManager getIMM(); public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) { dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback)); @@ -264,6 +270,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } void executeMessage(Message msg) { + ProtoOutputStream icProto; switch (msg.what) { case DO_GET_TEXT_AFTER_CURSOR: { Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); @@ -278,6 +285,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getTextAfterCursor(msg.arg1, msg.arg2); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1, + msg.arg2, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextAfterCursor", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -302,6 +315,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1, + msg.arg2, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextBeforeCursor", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -326,6 +345,11 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getSelectedText(msg.arg1); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSelectedText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -354,6 +378,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getSurroundingText(beforeLength, afterLength, flags); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength, + afterLength, flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSurroundingText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -378,6 +408,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getCursorCapsMode(msg.arg1); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1, + result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getCursorCapsMode", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -404,6 +440,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getExtractedText(request, msg.arg1); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetExtractedTextProto(request, + msg.arg1, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getExtractedText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 8c763a6efe549..b70348a953841 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -24,6 +24,9 @@ import android.inputmethodservice.AbstractInputMethodService; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.util.imetracing.ImeTracing; +import android.util.imetracing.InputConnectionHelper; +import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -87,8 +90,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetTextAfterCursorProto(length, + flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextAfterCursor", + inputMethodService, icProto); + } + + return result; } /** @@ -107,8 +120,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(length, + flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextBeforeCursor", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -127,8 +150,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetSelectedTextProto(flags, + result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getSelectedText", + inputMethodService, icProto); + } + + return result; } /** @@ -161,8 +194,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + SurroundingText result = Completable.getResultOrNull( value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetSurroundingTextProto( + beforeLength, afterLength, flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getSurroundingText", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -177,8 +220,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return 0; } - return Completable.getResultOrZero( + int result = Completable.getResultOrZero( value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetCursorCapsModeProto( + reqModes, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getCursorCapsMode", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -193,8 +246,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + ExtractedText result = Completable.getResultOrNull( value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetExtractedTextProto( + request, flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getExtractedText", + inputMethodService, icProto); + } + + return result; } @AnyThread diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index d30d662806d49..6cdf0df17b65c 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -28,6 +28,7 @@ import android.text.Editable; import android.text.Selection; import android.text.method.KeyListener; import android.util.Log; +import android.util.imetracing.InputConnectionHelper; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; @@ -45,7 +46,6 @@ import android.widget.TextView; 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; @@ -243,7 +243,7 @@ public class EditableInputConnection extends BaseInputConnection final long token = proto.start(fieldId); CharSequence editableText = mTextView.getText(); CharSequence selectedText = getSelectedText(0 /* flags */); - if (DUMP_TEXT) { + if (InputConnectionHelper.DUMP_TEXT) { if (editableText != null) { proto.write(EDITABLE_TEXT, editableText.toString()); } diff --git a/core/proto/android/inputmethodservice/inputmethodservice.proto b/core/proto/android/inputmethodservice/inputmethodservice.proto index e5d1713616953..1f68fb4b513da 100644 --- a/core/proto/android/inputmethodservice/inputmethodservice.proto +++ b/core/proto/android/inputmethodservice/inputmethodservice.proto @@ -18,6 +18,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/inputmethodservice/softinputwindow.proto"; import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto"; package android.inputmethodservice; @@ -51,6 +52,7 @@ message InputMethodServiceProto { optional int32 status_icon = 25; optional InsetsProto last_computed_insets = 26; optional string settings_observer = 27; + optional .android.view.inputmethod.InputConnectionCallProto input_connection_call = 28; message InsetsProto { optional int32 content_top_insets = 1; diff --git a/core/proto/android/view/inputmethod/inputconnection.proto b/core/proto/android/view/inputmethod/inputconnection.proto index ad9a95aa95e6a..d1f257ff2c5c4 100644 --- a/core/proto/android/view/inputmethod/inputconnection.proto +++ b/core/proto/android/view/inputmethod/inputconnection.proto @@ -31,4 +31,68 @@ message InputConnectionProto { optional int32 selected_text_start = 3; optional int32 selected_text_end = 4; optional int32 cursor_caps_mode = 5; +} + +/** + * Shows information about parameters and result for method calls to + * {@link android.view.inputmethod.InputConnection}. + */ +message InputConnectionCallProto { + oneof method_call { + GetTextBeforeCursor get_text_before_cursor = 1; + GetTextAfterCursor get_text_after_cursor = 2; + GetSelectedText get_selected_text = 3; + GetSurroundingText get_surrounding_text = 4; + GetCursorCapsMode get_cursor_caps_mode = 5; + GetExtractedText get_extracted_text = 6; + } + + message GetTextBeforeCursor { + optional int32 length = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetTextAfterCursor { + optional int32 length = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetSelectedText { + optional int32 flags = 1; + optional string result = 2 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetSurroundingText { + optional int32 before_length = 1; + optional int32 after_length = 2; + optional int32 flags = 3; + optional SurroundingText result = 4; + + message SurroundingText { + optional string text = 1 [(.android.privacy).dest = DEST_LOCAL]; + optional int32 selection_start = 2; + optional int32 selection_end = 3; + optional int32 offset = 4; + } + } + + message GetCursorCapsMode { + optional int32 req_modes = 1; + optional int32 result = 2; + } + + message GetExtractedText { + optional ExtractedTextRequest request = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + + message ExtractedTextRequest { + optional int32 token = 1; + optional int32 flags = 2; + optional int32 hint_max_lines = 3; + optional int32 hint_max_chars = 4; + } + } } \ 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 c1dce6f2d093b..8e4377ca124cf 100644 --- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -72,6 +72,7 @@ message InputMethodClientsTraceProto { optional EditorInfoProto editor_info = 6; optional ImeFocusControllerProto ime_focus_controller = 7; optional InputConnectionProto input_connection = 8; + optional InputConnectionCallProto input_connection_call = 9; } }