Introduce InputMethodServiceInternal for better abstraction

This is a mechanical refactoring CL that has no behavior change.

This CL removes the direct dependency on AbstractInputMethodService
from whenever possible.  As a result, the following classes no longer
directly depend on AbstractInputMethodService.

 * android.inputmethodservice.IInputMethodWrapper
 * android.inputmethodservice.RemoteInputConnection
 * com.android.internal.inputmethod.ImeTracing
 * com.android.internal.inputmethod.ImeTracingClientImpl
 * com.android.internal.inputmethod.ImeTracingServerImpl

This is still a purely mechanical refactoring.  There should be no
observable behavior change.

Bug: 192412909
Test: atest CtsInputMethodTestCases
Test: Manually verified that IME tracing still works
Change-Id: I2aeeeacd27195ce10059d6590e098a4a969e774d
This commit is contained in:
Yohei Yukawa
2021-07-09 09:46:58 -07:00
parent 26b594af10
commit a975bfc447
8 changed files with 288 additions and 186 deletions

View File

@@ -25,13 +25,10 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import android.window.WindowProviderService;
@@ -215,16 +212,6 @@ public abstract class AbstractInputMethodService extends WindowProviderService
*/
public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
/**
* 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, @Nullable byte[] icProto);
/**
* Implement this to handle {@link android.os.Binder#dump Binder.dump()}
* calls on your input method.
@@ -238,9 +225,36 @@ public abstract class AbstractInputMethodService extends WindowProviderService
if (mInputMethod == null) {
mInputMethod = onCreateInputMethodInterface();
}
return new IInputMethodWrapper(this, mInputMethod);
return new IInputMethodWrapper(createInputMethodServiceInternal(), mInputMethod);
}
/**
* Used to inject custom {@link InputMethodServiceInternal}.
*
* @return the {@link InputMethodServiceInternal} to be used.
*/
@NonNull
InputMethodServiceInternal createInputMethodServiceInternal() {
return new InputMethodServiceInternal() {
/**
* {@inheritDoc}
*/
@NonNull
@Override
public Context getContext() {
return AbstractInputMethodService.this;
}
/**
* {@inheritDoc}
*/
@Override
public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
AbstractInputMethodService.this.dump(fd, fout, args);
}
};
}
/**
* Implement this to handle trackball events on your input method.
*
@@ -263,35 +277,6 @@ public abstract class AbstractInputMethodService extends WindowProviderService
return false;
}
/**
* Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
* permission to the content.
*
* <p>Default implementation does nothing.</p>
*
* @param inputContentInfo Content to be temporarily exposed from the input method to the
* application.
* This cannot be {@code null}.
* @param inputConnection {@link InputConnection} with which
* {@link InputConnection#commitContent(InputContentInfo, int, android.os.Bundle)} will be
* called.
* @return {@code false} if we cannot allow a temporary access permission.
* @hide
*/
public void exposeContent(@NonNull InputContentInfo inputContentInfo,
@NonNull InputConnection inputConnection) {
return;
}
/**
* Called when the user took some actions that should be taken into consideration to update the
* MRU list for input method rotation.
*
* @hide
*/
public void notifyUserActionIfNecessary() {
}
// TODO(b/149463653): remove it in T. We missed the API deadline in S.
/** @hide */
@Override

View File

@@ -76,7 +76,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;
final WeakReference<AbstractInputMethodService> mTarget;
final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@UnsupportedAppUsage
final HandlerCaller mCaller;
@@ -129,12 +129,12 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
}
public IInputMethodWrapper(AbstractInputMethodService context, InputMethod inputMethod) {
mTarget = new WeakReference<>(context);
mContext = context.getApplicationContext();
IInputMethodWrapper(InputMethodServiceInternal imsInternal, InputMethod inputMethod) {
mTarget = new WeakReference<>(imsInternal);
mContext = imsInternal.getContext().getApplicationContext();
mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
mInputMethod = new WeakReference<>(inputMethod);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mTargetSdkVersion = imsInternal.getContext().getApplicationInfo().targetSdkVersion;
}
@MainThread
@@ -149,7 +149,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
switch (msg.what) {
case DO_DUMP: {
AbstractInputMethodService target = mTarget.get();
InputMethodServiceInternal target = mTarget.get();
if (target == null) {
return;
}
@@ -251,11 +251,11 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
AbstractInputMethodService target = mTarget.get();
InputMethodServiceInternal target = mTarget.get();
if (target == null) {
return;
}
if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
fout.println("Permission Denial: can't dump InputMethodManager from from pid="

View File

@@ -55,7 +55,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACK
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
@@ -740,7 +739,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this,
"InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
@@ -797,7 +796,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this,
"InputMethodService.InputMethodImpl#showSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
@@ -2222,7 +2221,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this,
ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
null /* icProto */);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
mDecorViewWasVisible = mDecorViewVisible;
@@ -2301,7 +2300,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this,
"InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
null /* icProto */);
if (setVisible) {
cancelImeSurfaceRemoval();
@@ -2330,7 +2329,7 @@ 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", mDumper,
null /* icProto */);
mWindowVisible = false;
finishViews(false /* finishingInput */);
@@ -2403,7 +2402,7 @@ 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", mDumper,
null /* icProto */);
finishViews(true /* finishingInput */);
if (mInputStarted) {
@@ -2420,7 +2419,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (!restarting && mInputStarted) {
doFinishInput();
}
ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this,
ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", mDumper,
null /* icProto */);
mInputStarted = true;
mStartedInputConnection = ic;
@@ -2580,7 +2579,7 @@ 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", mDumper,
null /* icProto */);
mPrivOps.hideMySoftInput(flags);
}
@@ -2594,7 +2593,7 @@ 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", mDumper,
null /* icProto */);
mPrivOps.showMySoftInput(flags);
}
@@ -3281,67 +3280,91 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
* Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
* permission to the content.
* Used to inject custom {@link InputMethodServiceInternal}.
*
* @param inputContentInfo Content to be temporarily exposed from the input method to the
* application.
* This cannot be {@code null}.
* @param inputConnection {@link InputConnection} with which
* {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
* @hide
* @return the {@link InputMethodServiceInternal} to be used.
*/
@NonNull
@Override
public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
@NonNull InputConnection inputConnection) {
if (inputConnection == null) {
return;
}
if (getCurrentInputConnection() != inputConnection) {
return;
}
exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
}
/**
* {@inheritDoc}
* @hide
*/
@AnyThread
@Override
public final void notifyUserActionIfNecessary() {
synchronized (mLock) {
if (mNotifyUserActionSent) {
return;
final InputMethodServiceInternal createInputMethodServiceInternal() {
return new InputMethodServiceInternal() {
/**
* {@inheritDoc}
*/
@NonNull
@Override
public Context getContext() {
return InputMethodService.this;
}
mPrivOps.notifyUserActionAsync();
mNotifyUserActionSent = true;
}
}
/**
* Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
* permission to the content.
*
* <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
* InputConnection)} for details.</p>
*
* @param inputContentInfo Content to be temporarily exposed from the input method to the
* application.
* This cannot be {@code null}.
* @param editorInfo The editor that receives {@link InputContentInfo}.
*/
private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
@NonNull EditorInfo editorInfo) {
final Uri contentUri = inputContentInfo.getContentUri();
final IInputContentUriToken uriToken =
mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
if (uriToken == null) {
Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
+ " packageName=" + editorInfo.packageName);
return;
}
inputContentInfo.setUriToken(uriToken);
/**
* {@inheritDoc}
*/
@Override
public void exposeContent(@NonNull InputContentInfo inputContentInfo,
@NonNull InputConnection inputConnection) {
if (inputConnection == null) {
return;
}
if (getCurrentInputConnection() != inputConnection) {
return;
}
exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
}
/**
* {@inheritDoc}
*/
@Override
public void notifyUserActionIfNecessary() {
synchronized (mLock) {
if (mNotifyUserActionSent) {
return;
}
mPrivOps.notifyUserActionAsync();
mNotifyUserActionSent = true;
}
}
/**
* Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
* permission to the content.
*
* <p>See {@link #exposeContent(InputContentInfo, InputConnection)} for details.</p>
*
* @param inputContentInfo Content to be temporarily exposed from the input method to
* the application. This cannot be {@code null}.
* @param editorInfo The editor that receives {@link InputContentInfo}.
*/
private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
@NonNull EditorInfo editorInfo) {
final Uri contentUri = inputContentInfo.getContentUri();
final IInputContentUriToken uriToken =
mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
if (uriToken == null) {
Log.e(TAG, "createInputContentAccessToken failed. contentUri="
+ contentUri.toString() + " packageName=" + editorInfo.packageName);
return;
}
inputContentInfo.setUriToken(uriToken);
}
/**
* {@inheritDoc}
*/
@Override
public void dump(FileDescriptor fd, PrintWriter fout, String[]args) {
InputMethodService.this.dump(fd, fout, args);
}
/**
* {@inheritDoc}
*/
@Override
public void triggerServiceDump(String where, @Nullable byte[] icProto) {
ImeTracing.getInstance().triggerServiceDump(where, mDumper, icProto);
}
};
}
private int mapToImeWindowStatus() {
@@ -3411,42 +3434,44 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mSettingsObserver=" + mSettingsObserver);
}
/**
* @hide
*/
@Override
public final void dumpProtoInternal(ProtoOutputStream proto, byte[] icProto) {
final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
proto.write(VIEWS_CREATED, mViewsCreated);
proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
proto.write(WINDOW_VISIBLE, mWindowVisible);
proto.write(IN_SHOW_WINDOW, mInShowWindow);
proto.write(CONFIGURATION, getResources().getConfiguration().toString());
proto.write(TOKEN, Objects.toString(mToken));
proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
proto.write(INPUT_STARTED, mInputStarted);
proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
if (mInputEditorInfo != null) {
mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
/**
* {@inheritDoc}
*/
@Override
public void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto) {
final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
proto.write(VIEWS_CREATED, mViewsCreated);
proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
proto.write(WINDOW_VISIBLE, mWindowVisible);
proto.write(IN_SHOW_WINDOW, mInShowWindow);
proto.write(CONFIGURATION, getResources().getConfiguration().toString());
proto.write(TOKEN, Objects.toString(mToken));
proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
proto.write(INPUT_STARTED, mInputStarted);
proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
if (mInputEditorInfo != null) {
mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
}
proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
proto.write(IS_FULLSCREEN, mIsFullscreen);
proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
proto.write(EXTRACTED_TOKEN, mExtractedToken);
proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
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);
}
proto.end(token);
}
proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
proto.write(IS_FULLSCREEN, mIsFullscreen);
proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
proto.write(EXTRACTED_TOKEN, mExtractedToken);
proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
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);
}
proto.end(token);
}
};
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2021 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.inputmethodservice;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* A set of internal methods exposed by {@link InputMethodService} to be called only from other
* framework classes for internal use.
*
* <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p>
*/
interface InputMethodServiceInternal {
/**
* @return {@link Context} associated with the service.
*/
@NonNull
Context getContext();
/**
* Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
* permission to the content.
*
* @param inputContentInfo Content to be temporarily exposed from the input method to the
* application. This cannot be {@code null}.
* @param inputConnection {@link InputConnection} with which
* {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}
* will be called.
*/
default void exposeContent(@NonNull InputContentInfo inputContentInfo,
@NonNull InputConnection inputConnection) {
}
/**
* Called when the user took some actions that should be taken into consideration to update the
* MRU list for input method rotation.
*/
default void notifyUserActionIfNecessary() {
}
/**
* Called when the system is asking the IME to dump its information for debugging.
*
* <p>The caller is responsible for checking {@link android.Manifest.permission.DUMP}.</p>
*
* @param fd The raw file descriptor that the dump is being sent to.
* @param fout The file to which you should dump your state. This will be
* closed for you after you return.
* @param args additional arguments to the dump request.
*/
default void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
}
/**
* Called with {@link com.android.internal.inputmethod.ImeTracing#triggerServiceDump(String,
* com.android.internal.inputmethod.ImeTracing.ServiceDumper, byte[])} needs to be triggered
* with the given parameters.
*
* @param where {@code where} parameter to be passed.
* @param icProto {@code icProto} parameter to be passed.
*/
default void triggerServiceDump(String where, @Nullable byte[] icProto) {
}
}

View File

@@ -52,7 +52,7 @@ final class RemoteInputConnection implements InputConnection {
private final IInputContextInvoker mInvoker;
@NonNull
private final WeakReference<AbstractInputMethodService> mInputMethodService;
private final WeakReference<InputMethodServiceInternal> mInputMethodService;
@MissingMethodFlags
private final int mMissingMethods;
@@ -67,7 +67,7 @@ final class RemoteInputConnection implements InputConnection {
private final CancellationGroup mCancellationGroup;
RemoteInputConnection(
@NonNull WeakReference<AbstractInputMethodService> inputMethodService,
@NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
IInputContext inputContext, @MissingMethodFlags int missingMethods,
@NonNull CancellationGroup cancellationGroup) {
mInputMethodService = inputMethodService;
@@ -90,12 +90,11 @@ final class RemoteInputConnection implements InputConnection {
final CharSequence result = Completable.getResultOrNull(
value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(length,
flags, result);
ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextAfterCursor",
inputMethodService, icProto);
inputMethodService.triggerServiceDump(TAG + "#getTextAfterCursor", icProto);
}
return result;
@@ -115,12 +114,11 @@ final class RemoteInputConnection implements InputConnection {
final CharSequence result = Completable.getResultOrNull(
value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(length,
flags, result);
ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextBeforeCursor",
inputMethodService, icProto);
inputMethodService.triggerServiceDump(TAG + "#getTextBeforeCursor", icProto);
}
return result;
@@ -140,12 +138,11 @@ final class RemoteInputConnection implements InputConnection {
final CharSequence result = Completable.getResultOrNull(
value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(flags,
result);
ImeTracing.getInstance().triggerServiceDump(TAG + "#getSelectedText",
inputMethodService, icProto);
inputMethodService.triggerServiceDump(TAG + "#getSelectedText", icProto);
}
return result;
@@ -179,12 +176,11 @@ final class RemoteInputConnection implements InputConnection {
final SurroundingText result = Completable.getResultOrNull(
value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
beforeLength, afterLength, flags, result);
ImeTracing.getInstance().triggerServiceDump(TAG + "#getSurroundingText",
inputMethodService, icProto);
inputMethodService.triggerServiceDump(TAG + "#getSurroundingText", icProto);
}
return result;
@@ -200,12 +196,11 @@ final class RemoteInputConnection implements InputConnection {
final int result = Completable.getResultOrZero(
value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
reqModes, result);
ImeTracing.getInstance().triggerServiceDump(TAG + "#getCursorCapsMode",
inputMethodService, icProto);
inputMethodService.triggerServiceDump(TAG + "#getCursorCapsMode", icProto);
}
return result;
@@ -221,12 +216,11 @@ final class RemoteInputConnection implements InputConnection {
final ExtractedText result = Completable.getResultOrNull(
value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
request, flags, result);
ImeTracing.getInstance().triggerServiceDump(TAG + "#getExtractedText",
inputMethodService, icProto);
inputMethodService.triggerServiceDump(TAG + "#getExtractedText", icProto);
}
return result;
@@ -243,7 +237,7 @@ final class RemoteInputConnection implements InputConnection {
@AnyThread
private void notifyUserActionIfNecessary() {
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService == null) {
// This basically should not happen, because it's the the caller of this method.
return;
@@ -395,7 +389,7 @@ final class RemoteInputConnection implements InputConnection {
}
if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
final AbstractInputMethodService inputMethodService = mInputMethodService.get();
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService == null) {
// This basically should not happen, because it's the caller of this method.
return false;

View File

@@ -19,7 +19,6 @@ package com.android.internal.inputmethod;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.Context;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -106,14 +105,30 @@ public abstract class ImeTracing {
public abstract void triggerClientDump(String where, InputMethodManager immInstance,
@Nullable byte[] icProto);
/**
* A delegate for {@link #triggerServiceDump(String, ServiceDumper, byte[])}.
*/
@FunctionalInterface
public interface ServiceDumper {
/**
* Dumps internal data into {@link ProtoOutputStream}.
*
* @param proto {@link ProtoOutputStream} to be dumped into.
* @param icProto {@link android.view.inputmethod.InputConnection} call data in proto
* format.
*/
void dumpToProto(ProtoOutputStream proto, @Nullable byte[] 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 dumper {@link ServiceDumper} to be used to dump
* {@link android.inputmethodservice.InputMethodService}.
* @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, ServiceDumper dumper,
@Nullable byte[] icProto);
/**

View File

@@ -18,7 +18,6 @@ package com.android.internal.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
@@ -65,7 +64,7 @@ class ImeTracingClientImpl extends ImeTracing {
}
@Override
public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service,
public void triggerServiceDump(String where, @NonNull ServiceDumper dumper,
@Nullable byte[] icProto) {
if (!isEnabled() || !isAvailable()) {
return;
@@ -80,7 +79,7 @@ class ImeTracingClientImpl extends ImeTracing {
try {
ProtoOutputStream proto = new ProtoOutputStream();
service.dumpProtoInternal(proto, icProto);
dumper.dumpToProto(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);

View File

@@ -19,7 +19,6 @@ package com.android.internal.inputmethod;
import static android.os.Build.IS_USER;
import android.annotation.Nullable;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
@@ -112,8 +111,7 @@ class ImeTracingServerImpl extends ImeTracing {
}
@Override
public void triggerServiceDump(String where, AbstractInputMethodService service,
@Nullable byte[] icProto) {
public void triggerServiceDump(String where, ServiceDumper dumper, @Nullable byte[] icProto) {
// Intentionally left empty, this is implemented in ImeTracingClientImpl
}