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

Commit 930328ca authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Annotate threads for some IME handling methods

This is a preparation to work on Bug 36897707.

For instance, the reason why most of IME-related callbacks in
InputMethodService get called on the main thread is because
IInputMethodWrapper keeps forwarding incoming IPCs into the
main looper of the IME process as follows:

  InputMethodManagerService (IMMS)
        ------
    -> one-way binder IPCs over	IInputMethod
        ------
      -> IInputMethodWrapper (on the binder thread(s))
       -> Handler (to dispatch tasks to main thread)
        -> InputMethodImpl.* (on the main thread)
         -> InputMethodService.* (on the main thread)

By adding explicit annotations such as @BinderThread and @MainThread
in relevant methods, this CL makes that kind of investigation much
easier than before.

Bug: 36897707
Test: compile
Change-Id: I8f9afe9a1986a9fa41fb66fdc64e8f0f67e45c2e
parent 16f04077
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.inputmethodservice;

import android.annotation.MainThread;
import android.annotation.NonNull;
import android.app.Service;
import android.content.Intent;
@@ -62,6 +63,7 @@ public abstract class AbstractInputMethodService extends Service
         * back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
         * AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
         */
        @MainThread
        public void createSession(SessionCallback callback) {
            callback.sessionCreated(onCreateInputMethodSessionInterface());
        }
@@ -71,6 +73,7 @@ public abstract class AbstractInputMethodService extends Service
         * {@link AbstractInputMethodSessionImpl#revokeSelf()
         * AbstractInputMethodSessionImpl.setEnabled()} method.
         */
        @MainThread
        public void setSessionEnabled(InputMethodSession session, boolean enabled) {
            ((AbstractInputMethodSessionImpl)session).setEnabled(enabled);
        }
@@ -80,6 +83,7 @@ public abstract class AbstractInputMethodService extends Service
         * {@link AbstractInputMethodSessionImpl#revokeSelf()
         * AbstractInputMethodSessionImpl.revokeSelf()} method.
         */
        @MainThread
        public void revokeSession(InputMethodSession session) {
            ((AbstractInputMethodSessionImpl)session).revokeSelf();
        }
+14 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.inputmethodservice;

import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -113,6 +115,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
    }

    @MainThread
    @Override
    public void executeMessage(Message msg) {
        InputMethod inputMethod = mInputMethod.get();
@@ -196,6 +199,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
        Log.w(TAG, "Unhandled message code: " + msg.what);
    }

    @BinderThread
    @Override
    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
        AbstractInputMethodService target = mTarget.get();
@@ -223,11 +227,13 @@ class IInputMethodWrapper extends IInputMethod.Stub
        }
    }

    @BinderThread
    @Override
    public void attachToken(IBinder token) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
    }

    @BinderThread
    @Override
    public void bindInput(InputBinding binding) {
        // This IInputContext is guaranteed to implement all the methods.
@@ -238,11 +244,13 @@ class IInputMethodWrapper extends IInputMethod.Stub
        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
    }

    @BinderThread
    @Override
    public void unbindInput() {
        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
    }

    @BinderThread
    @Override
    public void startInput(IBinder startInputToken, IInputContext inputContext,
            @InputConnectionInspector.MissingMethodFlags final int missingMethods,
@@ -251,12 +259,14 @@ class IInputMethodWrapper extends IInputMethod.Stub
                missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute));
    }

    @BinderThread
    @Override
    public void createSession(InputChannel channel, IInputSessionCallback callback) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
                channel, callback));
    }

    @BinderThread
    @Override
    public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
        try {
@@ -273,6 +283,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
        }
    }

    @BinderThread
    @Override
    public void revokeSession(IInputMethodSession session) {
        try {
@@ -288,18 +299,21 @@ class IInputMethodWrapper extends IInputMethod.Stub
        }
    }

    @BinderThread
    @Override
    public void showSoftInput(int flags, ResultReceiver resultReceiver) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
                flags, resultReceiver));
    }

    @BinderThread
    @Override
    public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
                flags, resultReceiver));
    }

    @BinderThread
    @Override
    public void changeInputMethodSubtype(InputMethodSubtype subtype) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
+9 −0
Original line number Diff line number Diff line
@@ -384,6 +384,7 @@ public class InputMethodService extends AbstractInputMethodService {
        /**
         * {@inheritDoc}
         */
        @MainThread
        @Override
        public void attachToken(IBinder token) {
            if (mToken == null) {
@@ -397,6 +398,7 @@ public class InputMethodService extends AbstractInputMethodService {
         *
         * <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
         */
        @MainThread
        @Override
        public void bindInput(InputBinding binding) {
            mInputBinding = binding;
@@ -415,6 +417,7 @@ public class InputMethodService extends AbstractInputMethodService {
         *
         * <p>Calls {@link InputMethodService#onUnbindInput()} when done.</p>
         */
        @MainThread
        @Override
        public void unbindInput() {
            if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
@@ -427,6 +430,7 @@ public class InputMethodService extends AbstractInputMethodService {
        /**
         * {@inheritDoc}
         */
        @MainThread
        @Override
        public void startInput(InputConnection ic, EditorInfo attribute) {
            if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
@@ -436,6 +440,7 @@ public class InputMethodService extends AbstractInputMethodService {
        /**
         * {@inheritDoc}
         */
        @MainThread
        @Override
        public void restartInput(InputConnection ic, EditorInfo attribute) {
            if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
@@ -446,6 +451,7 @@ public class InputMethodService extends AbstractInputMethodService {
         * {@inheritDoc}
         * @hide
         */
        @MainThread
        @Override
        public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
                @NonNull EditorInfo editorInfo, boolean restarting,
@@ -462,6 +468,7 @@ public class InputMethodService extends AbstractInputMethodService {
        /**
         * {@inheritDoc}
         */
        @MainThread
        @Override
        public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
            if (DEBUG) Log.v(TAG, "hideSoftInput()");
@@ -481,6 +488,7 @@ public class InputMethodService extends AbstractInputMethodService {
        /**
         * {@inheritDoc}
         */
        @MainThread
        @Override
        public void showSoftInput(int flags, ResultReceiver resultReceiver) {
            if (DEBUG) Log.v(TAG, "showSoftInput()");
@@ -513,6 +521,7 @@ public class InputMethodService extends AbstractInputMethodService {
        /**
         * {@inheritDoc}
         */
        @MainThread
        @Override
        public void changeInputMethodSubtype(InputMethodSubtype subtype) {
            onCurrentInputMethodSubtypeChanged(subtype);
+14 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view.inputmethod;

import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -90,6 +91,7 @@ public interface InputMethod {
     * accept the first token given to you.  Any after that may come from the
     * client.
     */
    @MainThread
    public void attachToken(IBinder token);

    /**
@@ -104,6 +106,7 @@ public interface InputMethod {
     * @see InputBinding
     * @see #unbindInput()
     */
    @MainThread
    public void bindInput(InputBinding binding);

    /**
@@ -114,6 +117,7 @@ public interface InputMethod {
     * Typically this method is called when the application changes to be
     * non-foreground.
     */
    @MainThread
    public void unbindInput();

    /**
@@ -129,6 +133,7 @@ public interface InputMethod {
     * 
     * @see EditorInfo
     */
    @MainThread
    public void startInput(InputConnection inputConnection, EditorInfo info);

    /**
@@ -147,6 +152,7 @@ public interface InputMethod {
     * 
     * @see EditorInfo
     */
    @MainThread
    public void restartInput(InputConnection inputConnection, EditorInfo attribute);

    /**
@@ -177,6 +183,7 @@ public interface InputMethod {
     * @see EditorInfo
     * @hide
     */
    @MainThread
    default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
            @NonNull EditorInfo editorInfo, boolean restarting,
            @NonNull IBinder startInputToken) {
@@ -195,6 +202,7 @@ public interface InputMethod {
     * 
     * @param callback Interface that is called with the newly created session.
     */
    @MainThread
    public void createSession(SessionCallback callback);
    
    /**
@@ -203,6 +211,7 @@ public interface InputMethod {
     * @param session The {@link InputMethodSession} previously provided through
     * SessionCallback.sessionCreated() that is to be changed.
     */
    @MainThread
    public void setSessionEnabled(InputMethodSession session, boolean enabled);
    
    /**
@@ -214,6 +223,7 @@ public interface InputMethod {
     * @param session The {@link InputMethodSession} previously provided through
     * SessionCallback.sessionCreated() that is to be revoked.
     */
    @MainThread
    public void revokeSession(InputMethodSession session);
    
    /**
@@ -244,6 +254,7 @@ public interface InputMethod {
     * {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or
     * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
     */
    @MainThread
    public void showSoftInput(int flags, ResultReceiver resultReceiver);
    
    /**
@@ -258,11 +269,13 @@ public interface InputMethod {
     * {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or
     * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
     */
    @MainThread
    public void hideSoftInput(int flags, ResultReceiver resultReceiver);

    /**
     * Notify that the input method subtype is being changed in the same input method.
     * @param subtype New subtype of the notified input method
     */
    @MainThread
    public void changeInputMethodSubtype(InputMethodSubtype subtype);
}
+47 −7
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.internal.view;

import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.Bundle;
@@ -67,6 +69,7 @@ public class InputConnectionWrapper implements InputConnection {
         * sequence number is set to a new integer.  We use a sequence number so that replies that
         * occur after a timeout has expired are not interpreted as replies to a later request.
         */
        @AnyThread
        private static InputContextCallback getInstance() {
            synchronized (InputContextCallback.class) {
                // Return sInstance if it's non-null, otherwise construct a new callback
@@ -90,6 +93,7 @@ public class InputConnectionWrapper implements InputConnection {
        /**
         * Makes the given InputContextCallback available for use in the future.
         */
        @AnyThread
        private void dispose() {
            synchronized (InputContextCallback.class) {
                // If sInstance is non-null, just let this object be garbage-collected
@@ -103,6 +107,7 @@ public class InputConnectionWrapper implements InputConnection {
            }
        }

        @BinderThread
        public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
@@ -116,6 +121,7 @@ public class InputConnectionWrapper implements InputConnection {
            }
        }

        @BinderThread
        public void setTextAfterCursor(CharSequence textAfterCursor, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
@@ -129,6 +135,7 @@ public class InputConnectionWrapper implements InputConnection {
            }
        }

        @BinderThread
        public void setSelectedText(CharSequence selectedText, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
@@ -142,6 +149,7 @@ public class InputConnectionWrapper implements InputConnection {
            }
        }

        @BinderThread
        public void setCursorCapsMode(int capsMode, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
@@ -155,6 +163,7 @@ public class InputConnectionWrapper implements InputConnection {
            }
        }

        @BinderThread
        public void setExtractedText(ExtractedText extractedText, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
@@ -168,6 +177,7 @@ public class InputConnectionWrapper implements InputConnection {
            }
        }

        @BinderThread
        public void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
@@ -181,6 +191,7 @@ public class InputConnectionWrapper implements InputConnection {
            }
        }

        @BinderThread
        public void setCommitContentResult(boolean result, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
@@ -199,6 +210,7 @@ public class InputConnectionWrapper implements InputConnection {
         * 
         * <p>The caller must be synchronized on this callback object.
         */
        @AnyThread
        void waitForResultLocked() {
            long startTime = SystemClock.uptimeMillis();
            long endTime = startTime + MAX_WAIT_TIME_MILLIS;
@@ -225,6 +237,7 @@ public class InputConnectionWrapper implements InputConnection {
        mMissingMethods = missingMethods;
    }

    @AnyThread
    public CharSequence getTextAfterCursor(int length, int flags) {
        CharSequence value = null;
        try {
@@ -243,6 +256,7 @@ public class InputConnectionWrapper implements InputConnection {
        return value;
    }

    @AnyThread
    public CharSequence getTextBeforeCursor(int length, int flags) {
        CharSequence value = null;
        try {
@@ -261,6 +275,7 @@ public class InputConnectionWrapper implements InputConnection {
        return value;
    }

    @AnyThread
    public CharSequence getSelectedText(int flags) {
        if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) {
            // This method is not implemented.
@@ -283,6 +298,7 @@ public class InputConnectionWrapper implements InputConnection {
        return value;
    }

    @AnyThread
    public int getCursorCapsMode(int reqModes) {
        int value = 0;
        try {
@@ -301,6 +317,7 @@ public class InputConnectionWrapper implements InputConnection {
        return value;
    }

    @AnyThread
    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
        ExtractedText value = null;
        try {
@@ -319,6 +336,7 @@ public class InputConnectionWrapper implements InputConnection {
        return value;
    }

    @AnyThread
    public boolean commitText(CharSequence text, int newCursorPosition) {
        try {
            mIInputContext.commitText(text, newCursorPosition);
@@ -328,6 +346,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean commitCompletion(CompletionInfo text) {
        if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
            // This method is not implemented.
@@ -341,6 +360,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean commitCorrection(CorrectionInfo correctionInfo) {
        try {
            mIInputContext.commitCorrection(correctionInfo);
@@ -350,6 +370,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean setSelection(int start, int end) {
        try {
            mIInputContext.setSelection(start, end);
@@ -359,6 +380,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean performEditorAction(int actionCode) {
        try {
            mIInputContext.performEditorAction(actionCode);
@@ -368,6 +390,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean performContextMenuAction(int id) {
        try {
            mIInputContext.performContextMenuAction(id);
@@ -377,6 +400,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean setComposingRegion(int start, int end) {
        if (isMethodMissing(MissingMethodFlags.SET_COMPOSING_REGION)) {
            // This method is not implemented.
@@ -390,6 +414,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean setComposingText(CharSequence text, int newCursorPosition) {
        try {
            mIInputContext.setComposingText(text, newCursorPosition);
@@ -399,6 +424,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean finishComposingText() {
        try {
            mIInputContext.finishComposingText();
@@ -408,6 +434,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean beginBatchEdit() {
        try {
            mIInputContext.beginBatchEdit();
@@ -417,6 +444,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean endBatchEdit() {
        try {
            mIInputContext.endBatchEdit();
@@ -426,6 +454,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean sendKeyEvent(KeyEvent event) {
        try {
            mIInputContext.sendKeyEvent(event);
@@ -435,6 +464,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean clearMetaKeyStates(int states) {
        try {
            mIInputContext.clearMetaKeyStates(states);
@@ -444,6 +474,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
        try {
            mIInputContext.deleteSurroundingText(beforeLength, afterLength);
@@ -453,6 +484,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
        if (isMethodMissing(MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS)) {
            // This method is not implemented.
@@ -466,11 +498,13 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean reportFullscreenMode(boolean enabled) {
        // Nothing should happen when called from input method.
        return false;
    }

    @AnyThread
    public boolean performPrivateCommand(String action, Bundle data) {
        try {
            mIInputContext.performPrivateCommand(action, data);
@@ -480,6 +514,7 @@ public class InputConnectionWrapper implements InputConnection {
        }
    }

    @AnyThread
    public boolean requestCursorUpdates(int cursorUpdateMode) {
        boolean result = false;
        if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
@@ -502,15 +537,18 @@ public class InputConnectionWrapper implements InputConnection {
        return result;
    }

    @AnyThread
    public Handler getHandler() {
        // Nothing should happen when called from input method.
        return null;
    }

    @AnyThread
    public void closeConnection() {
        // Nothing should happen when called from input method.
    }

    @AnyThread
    public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
        boolean result = false;
        if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
@@ -542,10 +580,12 @@ public class InputConnectionWrapper implements InputConnection {
        return result;
    }

    @AnyThread
    private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
        return (mMissingMethods & methodFlag) == methodFlag;
    }

    @AnyThread
    @Override
    public String toString() {
        return "InputConnectionWrapper{idHash=#"
Loading