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

Commit 1112d366 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Deprecate IMMS#executeOrSendMessage()

This CL addresses the remaining TODO from Bug 215609403.

With this CL, IInputMethodClientInvoker takes over the responsibility
of what

  InputMethodManagerService#executeOrSendMessage()

has been doing so that InputMethodManagerService does not need to
worry about whether IInputMethodClient is a BinderProxy or not.

This CL is expected to be really safe.  At least for normal cases when
the IME client is not system_server, there is literally no semantic
change.

Bug: 234882948
Test: presubmit
Change-Id: I0a7dd5162c4b94599b896420b8427913fa772414
parent cc4c229e
Loading
Loading
Loading
Loading
+115 −4
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -30,6 +31,17 @@ import com.android.internal.inputmethod.InputBindResult;

/**
 * A stateless thin wrapper for {@link IInputMethodClient}.
 *
 * <p>This class also takes care of a special case when system_server is also an IME client. In this
 * scenario methods defined in {@link IInputMethodClient} are invoked as an direct (sync) invocation
 * despite the fact that all of them are marked {@code oneway}. This can easily cause dead lock
 * because {@link InputMethodManagerService} assumes that it's safe to make one-way IPCs while
 * holding the lock. This assumption becomes invalid when {@link IInputMethodClient} is not a
 * {@link android.os.BinderProxy}.</p>
 *
 * <p>To work around such a special scenario, this wrapper re-dispatch the method invocation into
 * the given {@link Handler} thread if {@link IInputMethodClient} is not a proxy object. Be careful
 * about its call ordering characteristics.</p>
 */
final class IInputMethodClientInvoker {
    private static final String TAG = InputMethodManagerService.TAG;
@@ -38,19 +50,25 @@ final class IInputMethodClientInvoker {
    @NonNull
    private final IInputMethodClient mTarget;
    private final boolean mIsProxy;
    @Nullable
    private final Handler mHandler;

    @AnyThread
    @Nullable
    static IInputMethodClientInvoker create(@Nullable IInputMethodClient inputMethodClient) {
    static IInputMethodClientInvoker create(@Nullable IInputMethodClient inputMethodClient,
            @NonNull Handler handler) {
        if (inputMethodClient == null) {
            return null;
        }
        return new IInputMethodClientInvoker(inputMethodClient);
        final boolean isProxy = Binder.isProxy(inputMethodClient);
        return new IInputMethodClientInvoker(inputMethodClient, isProxy, isProxy ? null : handler);
    }

    private IInputMethodClientInvoker(@NonNull IInputMethodClient target) {
    private IInputMethodClientInvoker(@NonNull IInputMethodClient target,
            boolean isProxy, @Nullable Handler handler) {
        mTarget = target;
        mIsProxy = Binder.isProxy(mTarget);
        mIsProxy = isProxy;
        mHandler = handler;
    }

    /**
@@ -76,6 +94,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void onBindMethod(@NonNull InputBindResult res) {
        if (mIsProxy) {
            onBindMethodInternal(res);
        } else {
            mHandler.post(() -> onBindMethodInternal(res));
        }
    }

    @AnyThread
    private void onBindMethodInternal(@NonNull InputBindResult res) {
        try {
            mTarget.onBindMethod(res);
        } catch (RemoteException e) {
@@ -91,6 +118,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void onBindAccessibilityService(@NonNull InputBindResult res, int id) {
        if (mIsProxy) {
            onBindAccessibilityServiceInternal(res, id);
        } else {
            mHandler.post(() -> onBindAccessibilityServiceInternal(res, id));
        }
    }

    @AnyThread
    private void onBindAccessibilityServiceInternal(@NonNull InputBindResult res, int id) {
        try {
            mTarget.onBindAccessibilityService(res, id);
        } catch (RemoteException e) {
@@ -106,6 +142,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void onUnbindMethod(int sequence, int unbindReason) {
        if (mIsProxy) {
            onUnbindMethodInternal(sequence, unbindReason);
        } else {
            mHandler.post(() -> onUnbindMethodInternal(sequence, unbindReason));
        }
    }

    @AnyThread
    private void onUnbindMethodInternal(int sequence, int unbindReason) {
        try {
            mTarget.onUnbindMethod(sequence, unbindReason);
        } catch (RemoteException e) {
@@ -115,6 +160,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void onUnbindAccessibilityService(int sequence, int id) {
        if (mIsProxy) {
            onUnbindAccessibilityServiceInternal(sequence, id);
        } else {
            mHandler.post(() -> onUnbindAccessibilityServiceInternal(sequence, id));
        }
    }

    @AnyThread
    private void onUnbindAccessibilityServiceInternal(int sequence, int id) {
        try {
            mTarget.onUnbindAccessibilityService(sequence, id);
        } catch (RemoteException e) {
@@ -124,6 +178,16 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void setActive(boolean active, boolean fullscreen, boolean reportToImeController) {
        if (mIsProxy) {
            setActiveInternal(active, fullscreen, reportToImeController);
        } else {
            mHandler.post(() -> setActiveInternal(active, fullscreen, reportToImeController));
        }
    }

    @AnyThread
    private void setActiveInternal(boolean active, boolean fullscreen,
            boolean reportToImeController) {
        try {
            mTarget.setActive(active, fullscreen, reportToImeController);
        } catch (RemoteException e) {
@@ -133,6 +197,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void scheduleStartInputIfNecessary(boolean fullscreen) {
        if (mIsProxy) {
            scheduleStartInputIfNecessaryInternal(fullscreen);
        } else {
            mHandler.post(() -> scheduleStartInputIfNecessaryInternal(fullscreen));
        }
    }

    @AnyThread
    private void scheduleStartInputIfNecessaryInternal(boolean fullscreen) {
        try {
            mTarget.scheduleStartInputIfNecessary(fullscreen);
        } catch (RemoteException e) {
@@ -142,6 +215,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void reportFullscreenMode(boolean fullscreen) {
        if (mIsProxy) {
            reportFullscreenModeInternal(fullscreen);
        } else {
            mHandler.post(() -> reportFullscreenModeInternal(fullscreen));
        }
    }

    @AnyThread
    private void reportFullscreenModeInternal(boolean fullscreen) {
        try {
            mTarget.reportFullscreenMode(fullscreen);
        } catch (RemoteException e) {
@@ -151,6 +233,17 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void updateVirtualDisplayToScreenMatrix(int bindSequence, float[] matrixValues) {
        if (mIsProxy) {
            updateVirtualDisplayToScreenMatrixInternal(bindSequence, matrixValues);
        } else {
            mHandler.post(() ->
                    updateVirtualDisplayToScreenMatrixInternal(bindSequence, matrixValues));
        }
    }

    @AnyThread
    private void updateVirtualDisplayToScreenMatrixInternal(int bindSequence,
            float[] matrixValues) {
        try {
            mTarget.updateVirtualDisplayToScreenMatrix(bindSequence, matrixValues);
        } catch (RemoteException e) {
@@ -160,6 +253,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void setImeTraceEnabled(boolean enabled) {
        if (mIsProxy) {
            setImeTraceEnabledInternal(enabled);
        } else {
            mHandler.post(() -> setImeTraceEnabledInternal(enabled));
        }
    }

    @AnyThread
    private void setImeTraceEnabledInternal(boolean enabled) {
        try {
            mTarget.setImeTraceEnabled(enabled);
        } catch (RemoteException e) {
@@ -169,6 +271,15 @@ final class IInputMethodClientInvoker {

    @AnyThread
    void throwExceptionFromSystem(String message) {
        if (mIsProxy) {
            throwExceptionFromSystemInternal(message);
        } else {
            mHandler.post(() -> throwExceptionFromSystemInternal(message));
        }
    }

    @AnyThread
    private void throwExceptionFromSystemInternal(String message) {
        try {
            mTarget.throwExceptionFromSystem(message);
        } catch (RemoteException e) {
+17 −130
Original line number Diff line number Diff line
@@ -244,13 +244,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    private static final int MSG_START_HANDWRITING = 1100;
    private static final int MSG_FINISH_HANDWRITING = 1110;

    private static final int MSG_UNBIND_CLIENT = 3000;
    private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 3001;
    private static final int MSG_BIND_CLIENT = 3010;
    private static final int MSG_BIND_ACCESSIBILITY_SERVICE = 3011;
    private static final int MSG_SET_ACTIVE = 3020;
    private static final int MSG_SET_INTERACTIVE = 3030;
    private static final int MSG_REPORT_FULLSCREEN_MODE = 3045;

    private static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;

@@ -488,10 +482,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}";
        }

        ClientState(IInputMethodClient _client, IRemoteInputConnection _fallbackInputConnection,
        ClientState(IInputMethodClientInvoker _client,
                IRemoteInputConnection _fallbackInputConnection,
                int _uid, int _pid, int _selfReportedDisplayId,
                ClientDeathRecipient _clientDeathRecipient) {
            client = IInputMethodClientInvoker.create(_client);
            client = _client;
            fallbackInputConnection = _fallbackInputConnection;
            uid = _uid;
            pid = _pid;
@@ -2452,8 +2447,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            // have the client crash.  Thus we do not verify the display ID at all here.  Instead we
            // later check the display ID every time the client needs to interact with the specified
            // display.
            mClients.put(client.asBinder(), new ClientState(client, inputConnection, callerUid,
                    callerPid, selfReportedDisplayId, deathRecipient));
            final IInputMethodClientInvoker clientInvoker =
                    IInputMethodClientInvoker.create(client, mHandler);
            mClients.put(client.asBinder(), new ClientState(clientInvoker, inputConnection,
                    callerUid, callerPid, selfReportedDisplayId, deathRecipient));
        }
    }

@@ -2497,57 +2494,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }
    }

    @NonNull
    private Message obtainMessageOO(int what, Object arg1, Object arg2) {
        final SomeArgs args = SomeArgs.obtain();
        args.arg1 = arg1;
        args.arg2 = arg2;
        return mHandler.obtainMessage(what, 0, 0, args);
    }

    @NonNull
    private Message obtainMessageOOO(int what, Object arg1, Object arg2, Object arg3) {
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = arg1;
        args.arg2 = arg2;
        args.arg3 = arg3;
        return mHandler.obtainMessage(what, 0, 0, args);
    }

    @NonNull
    private Message obtainMessageIIOO(int what, int arg1, int arg2,
            Object arg3, Object arg4) {
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = arg3;
        args.arg2 = arg4;
        return mHandler.obtainMessage(what, arg1, arg2, args);
    }

    @NonNull
    private Message obtainMessageIIIO(int what, int argi1, int argi2, int argi3, Object arg1) {
        final SomeArgs args = SomeArgs.obtain();
        args.arg1 = arg1;
        args.argi1 = argi1;
        args.argi2 = argi2;
        args.argi3 = argi3;
        return mHandler.obtainMessage(what, 0, 0, args);
    }

    private void executeOrSendMessage(IInputMethodClientInvoker target, Message msg) {
         if (target.asBinder() instanceof Binder) {
             // This is supposed to be emulating the one-way semantics when the IME client is
             // system_server itself, which has not been explicitly prohibited so far while we have
             // never ever officially supported such a use case...
             // We probably should create a simple wrapper of IInputMethodClient as the first step
             // to get rid of executeOrSendMessage() then should prohibit system_server to be the
             // IME client for long term.
             msg.sendToTarget();
         } else {
             handleMessage(msg);
             msg.recycle();
         }
    }

    @GuardedBy("ImfLock.class")
    void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
        if (mCurClient != null) {
@@ -2565,11 +2511,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            // Since we set active false to current client and set mCurClient to null, let's unbind
            // all accessibility too. That means, when input method get disconnected (including
            // switching ime), we also unbind accessibility
            scheduleSetActiveToClient(mCurClient, false /* active */, false /* fullscreen */,
            mCurClient.client.setActive(false /* active */, false /* fullscreen */,
                    false /* reportToImeController */);
            executeOrSendMessage(mCurClient.client, mHandler.obtainMessage(
                    MSG_UNBIND_CLIENT, getSequenceNumberLocked(), unbindClientReason,
                    mCurClient.client));
            mCurClient.client.onUnbindMethod(getSequenceNumberLocked(), unbindClientReason);
            mCurClient.sessionRequested = false;
            mCurClient.mSessionRequestedForAccessibility = false;
            mCurClient = null;
@@ -2901,7 +2845,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
        // If the screen is on, inform the new client it is active
        if (mIsInteractive) {
            scheduleSetActiveToClient(cs, true /* active */, false /* fullscreen */,
            cs.client.setActive(true /* active */, false /* fullscreen */,
                    false /* reportToImeController */);
        }
    }
@@ -3020,8 +2964,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                        attachNewAccessibilityLocked(StartInputReason.SESSION_CREATED_BY_IME,
                                true, -1);
                        if (res.method != null) {
                            executeOrSendMessage(mCurClient.client, obtainMessageOO(
                                    MSG_BIND_CLIENT, mCurClient.client, res));
                            mCurClient.client.onBindMethod(res);
                        }
                        return;
                    }
@@ -4529,7 +4472,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    public void reportVirtualDisplayGeometryAsync(IInputMethodClient parentClient,
            int childDisplayId, float[] matrixValues) {
        final IInputMethodClientInvoker parentClientInvoker =
                IInputMethodClientInvoker.create(parentClient);
                IInputMethodClientInvoker.create(parentClient, mHandler);
        try {
            final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(childDisplayId);
            if (displayInfo == null) {
@@ -4997,54 +4940,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

            // ---------------------------------------------------------

            case MSG_UNBIND_CLIENT: {
                // This unbinds all accessibility services too.
                ((IInputMethodClientInvoker) msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
                return true;
            }
            case MSG_UNBIND_ACCESSIBILITY_SERVICE: {
                args = (SomeArgs) msg.obj;
                IInputMethodClientInvoker client = (IInputMethodClientInvoker) args.arg1;
                int id = (int) args.arg2;
                client.onUnbindAccessibilityService(msg.arg1, id);
                args.recycle();
                return true;
            }
            case MSG_BIND_CLIENT: {
                args = (SomeArgs)msg.obj;
                IInputMethodClientInvoker client = (IInputMethodClientInvoker) args.arg1;
                InputBindResult res = (InputBindResult) args.arg2;
                client.onBindMethod(res);
                args.recycle();
                return true;
            }
            case MSG_BIND_ACCESSIBILITY_SERVICE: {
                args = (SomeArgs) msg.obj;
                IInputMethodClientInvoker client = (IInputMethodClientInvoker) args.arg1;
                InputBindResult res = (InputBindResult) args.arg2;
                int id = (int) args.arg3;
                client.onBindAccessibilityService(res, id);
                args.recycle();
                return true;
            }
            case MSG_SET_ACTIVE: {
                args = (SomeArgs) msg.obj;
                final ClientState clientState = (ClientState) args.arg1;
                clientState.client.setActive(args.argi1 != 0 /* active */,
                        args.argi2 != 0 /* fullScreen */,
                        args.argi3 != 0 /* reportToImeController */);
                args.recycle();
                return true;
            }
            case MSG_SET_INTERACTIVE:
                handleSetInteractive(msg.arg1 != 0);
                return true;
            case MSG_REPORT_FULLSCREEN_MODE: {
                final boolean fullscreen = msg.arg1 != 0;
                final ClientState clientState = (ClientState)msg.obj;
                clientState.client.reportFullscreenMode(fullscreen);
                return true;
            }

            // --------------------------------------------------------------
            case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
@@ -5138,19 +5036,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

            // Inform the current client of the change in active status
            if (mCurClient != null && mCurClient.client != null) {
                scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode,
                mCurClient.client.setActive(mIsInteractive, mInFullscreenMode,
                        mImePlatformCompatUtils.shouldFinishInputWithReportToIme(
                                getCurMethodUidLocked()));
            }
        }
    }

    private void scheduleSetActiveToClient(ClientState state, boolean active, boolean fullscreen,
            boolean reportToImeController) {
        executeOrSendMessage(state.client, obtainMessageIIIO(MSG_SET_ACTIVE,
                active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, state));
    }

    @GuardedBy("ImfLock.class")
    private boolean chooseNewDefaultIMELocked() {
        final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
@@ -5724,9 +5616,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    InputBindResult res = attachNewAccessibilityLocked(
                            StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY, true,
                            accessibilityConnectionId);
                    executeOrSendMessage(mCurClient.client, obtainMessageOOO(
                            MSG_BIND_ACCESSIBILITY_SERVICE, mCurClient.client, res,
                            accessibilityConnectionId));
                    mCurClient.client.onBindAccessibilityService(res, accessibilityConnectionId);
                }
            }
        }
@@ -5741,9 +5631,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    }
                    // A11yManagerService unbinds the disabled accessibility service. We don't need
                    // to do it here.
                    executeOrSendMessage(mCurClient.client, obtainMessageIIOO(
                            MSG_UNBIND_ACCESSIBILITY_SERVICE, getSequenceNumberLocked(),
                            0 /* unused */, mCurClient.client, accessibilityConnectionId));
                    mCurClient.client.onUnbindAccessibilityService(getSequenceNumberLocked(),
                            accessibilityConnectionId);
                }
                // We only have sessions when we bound to an input method. Remove this session
                // from all clients.
@@ -5837,9 +5726,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            }
            if (mCurClient != null && mCurClient.client != null) {
                mInFullscreenMode = fullscreen;
                executeOrSendMessage(mCurClient.client, mHandler.obtainMessage(
                        MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0 /* unused */,
                        mCurClient));
                mCurClient.client.reportFullscreenMode(fullscreen);
            }
        }
    }