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

Commit cfbd27ae authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Introduce IInputMethodClientInvoker"

parents b2583a9d 5fbf2e2f
Loading
Loading
Loading
Loading
+183 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 com.android.server.inputmethod;

import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;

import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.InputBindResult;

/**
 * A stateless thin wrapper for {@link IInputMethodClient}.
 */
final class IInputMethodClientInvoker {
    private static final String TAG = InputMethodManagerService.TAG;
    private static final boolean DEBUG = InputMethodManagerService.DEBUG;

    @NonNull
    private final IInputMethodClient mTarget;
    private final boolean mIsProxy;

    @AnyThread
    @Nullable
    static IInputMethodClientInvoker create(@Nullable IInputMethodClient inputMethodClient) {
        if (inputMethodClient == null) {
            return null;
        }
        return new IInputMethodClientInvoker(inputMethodClient);
    }

    private IInputMethodClientInvoker(@NonNull IInputMethodClient target) {
        mTarget = target;
        mIsProxy = Binder.isProxy(mTarget);
    }

    /**
     * A simplified version of {@link android.os.Debug#getCaller()}.
     *
     * @return method name of the caller.
     */
    @AnyThread
    private static String getCallerMethodName() {
        final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
        if (callStack.length <= 4) {
            return "<bottom of call stack>";
        }
        return callStack[4].getMethodName();
    }

    @AnyThread
    private static void logRemoteException(@NonNull RemoteException e) {
        if (DEBUG || !(e instanceof DeadObjectException)) {
            Slog.w(TAG, "IPC failed at IInputMethodClientInvoker#" + getCallerMethodName(), e);
        }
    }

    @AnyThread
    void onBindMethod(@NonNull InputBindResult res) {
        try {
            mTarget.onBindMethod(res);
        } catch (RemoteException e) {
            logRemoteException(e);
        } finally {
            // Dispose the channel if the input method is not local to this process
            // because the remote proxy will get its own copy when unparceled.
            if (res.channel != null && mIsProxy) {
                res.channel.dispose();
            }
        }
    }

    @AnyThread
    void onBindAccessibilityService(@NonNull InputBindResult res, int id) {
        try {
            mTarget.onBindAccessibilityService(res, id);
        } catch (RemoteException e) {
            logRemoteException(e);
        } finally {
            // Dispose the channel if the accessibility service is not local to this process
            // because the remote proxy will get its own copy when unparceled.
            if (res.channel != null && mIsProxy) {
                res.channel.dispose();
            }
        }
    }

    @AnyThread
    void onUnbindMethod(int sequence, int unbindReason) {
        try {
            mTarget.onUnbindMethod(sequence, unbindReason);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    void onUnbindAccessibilityService(int sequence, int id) {
        try {
            mTarget.onUnbindAccessibilityService(sequence, id);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    void setActive(boolean active, boolean fullscreen, boolean reportToImeController) {
        try {
            mTarget.setActive(active, fullscreen, reportToImeController);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    void scheduleStartInputIfNecessary(boolean fullscreen) {
        try {
            mTarget.scheduleStartInputIfNecessary(fullscreen);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    void reportFullscreenMode(boolean fullscreen) {
        try {
            mTarget.reportFullscreenMode(fullscreen);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    void updateVirtualDisplayToScreenMatrix(int bindSequence, float[] matrixValues) {
        try {
            mTarget.updateVirtualDisplayToScreenMatrix(bindSequence, matrixValues);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    void setImeTraceEnabled(boolean enabled) {
        try {
            mTarget.setImeTraceEnabled(enabled);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    void throwExceptionFromSystem(String message) {
        try {
            mTarget.throwExceptionFromSystem(message);
        } catch (RemoteException e) {
            logRemoteException(e);
        }
    }

    @AnyThread
    IBinder asBinder() {
        return mTarget.asBinder();
    }
}
+31 −103
Original line number Diff line number Diff line
@@ -96,7 +96,6 @@ import android.media.AudioManagerInternal;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -469,7 +468,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    }

    static final class ClientState {
        final IInputMethodClient client;
        final IInputMethodClientInvoker client;
        final IRemoteInputConnection fallbackInputConnection;
        final int uid;
        final int pid;
@@ -492,7 +491,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        ClientState(IInputMethodClient _client, IRemoteInputConnection _fallbackInputConnection,
                int _uid, int _pid, int _selfReportedDisplayId,
                ClientDeathRecipient _clientDeathRecipient) {
            client = _client;
            client = IInputMethodClientInvoker.create(_client);
            fallbackInputConnection = _fallbackInputConnection;
            uid = _uid;
            pid = _pid;
@@ -1619,10 +1618,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        final int mToUserId;

        @Nullable
        IInputMethodClient mClientToBeReset;
        IInputMethodClientInvoker mClientToBeReset;

        UserSwitchHandlerTask(InputMethodManagerService service, @UserIdInt int toUserId,
                @Nullable IInputMethodClient clientToBeReset) {
                @Nullable IInputMethodClientInvoker clientToBeReset) {
            mService = service;
            mToUserId = toUserId;
            mClientToBeReset = clientToBeReset;
@@ -1713,7 +1712,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

    @GuardedBy("ImfLock.class")
    void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
            @Nullable IInputMethodClient clientToBeReset) {
            @Nullable IInputMethodClientInvoker clientToBeReset) {
        if (mUserSwitchHandlerTask != null) {
            if (mUserSwitchHandlerTask.mToUserId == userId) {
                mUserSwitchHandlerTask.mClientToBeReset = clientToBeReset;
@@ -1880,7 +1879,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

    @GuardedBy("ImfLock.class")
    private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
            IInputMethodClient clientToBeReset) {
            IInputMethodClientInvoker clientToBeReset) {
        if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
                + " currentUserId=" + mSettings.getCurrentUserId());

@@ -1938,10 +1937,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                // The client is already gone.
                return;
            }
            try {
            cs.client.scheduleStartInputIfNecessary(mInFullscreenMode);
            } catch (RemoteException e) {
            }
        }
    }

@@ -2537,7 +2533,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        return mHandler.obtainMessage(what, 0, 0, args);
    }

    private void executeOrSendMessage(IInputMethodClient target, Message msg) {
    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
@@ -4532,6 +4528,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    @Override
    public void reportVirtualDisplayGeometryAsync(IInputMethodClient parentClient,
            int childDisplayId, float[] matrixValues) {
        final IInputMethodClientInvoker parentClientInvoker =
                IInputMethodClientInvoker.create(parentClient);
        try {
            final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(childDisplayId);
            if (displayInfo == null) {
@@ -4544,7 +4542,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            }

            synchronized (ImfLock.class) {
                final ClientState cs = mClients.get(parentClient.asBinder());
                final ClientState cs = mClients.get(parentClientInvoker.asBinder());
                if (cs == null) {
                    return;
                }
@@ -4601,15 +4599,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                        if (needToNotify) {
                            final float[] values = new float[9];
                            matrix.getValues(values);
                            try {
                            mCurClient.client.updateVirtualDisplayToScreenMatrix(
                                    getSequenceNumberLocked(), values);
                            } catch (RemoteException e) {
                                Slog.e(TAG,
                                        "Exception calling updateVirtualDisplayToScreenMatrix()",
                                        e);

                            }
                        }
                        break;
                    }
@@ -4617,12 +4608,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                }
            }
        } catch (Throwable t) {
            if (parentClient != null) {
                try {
                    parentClient.throwExceptionFromSystem(t.toString());
                } catch (RemoteException e) {
                    Slog.e(TAG, "Exception calling throwExceptionFromSystem()", e);
                }
            if (parentClientInvoker != null) {
                parentClientInvoker.throwExceptionFromSystem(t.toString());
            }
        }
    }
@@ -4702,11 +4689,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }
        for (ClientState state : clients.values()) {
            if (state != null) {
                try {
                state.client.setImeTraceEnabled(true /* enabled */);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error while trying to enable ime trace on client window", e);
                }
            }
        }
    }
@@ -4721,11 +4704,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }
        for (ClientState state : clients.values()) {
            if (state != null) {
                try {
                state.client.setImeTraceEnabled(false /* enabled */);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error while trying to disable ime trace on client window", e);
                }
            }
        }
    }
@@ -5019,81 +4998,41 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            // ---------------------------------------------------------

            case MSG_UNBIND_CLIENT: {
                try {
                // This unbinds all accessibility services too.
                    ((IInputMethodClient) msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
                } catch (RemoteException e) {
                    // There is nothing interesting about the last client dying.
                    if (!(e instanceof DeadObjectException)) {
                        Slog.w(TAG, "RemoteException when unbinding input method service or"
                                + "accessibility services");
                    }
                }
                ((IInputMethodClientInvoker) msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
                return true;
            }
            case MSG_UNBIND_ACCESSIBILITY_SERVICE: {
                args = (SomeArgs) msg.obj;
                IInputMethodClient client = (IInputMethodClient) args.arg1;
                IInputMethodClientInvoker client = (IInputMethodClientInvoker) args.arg1;
                int id = (int) args.arg2;
                try {
                client.onUnbindAccessibilityService(msg.arg1, id);
                } catch (RemoteException e) {
                    // There is nothing interesting about the last client dying.
                    if (!(e instanceof DeadObjectException)) {
                        Slog.w(TAG, "RemoteException when unbinding accessibility services");
                    }
                }
                args.recycle();
                return true;
            }
            case MSG_BIND_CLIENT: {
                args = (SomeArgs)msg.obj;
                IInputMethodClient client = (IInputMethodClient)args.arg1;
                IInputMethodClientInvoker client = (IInputMethodClientInvoker) args.arg1;
                InputBindResult res = (InputBindResult) args.arg2;
                try {
                client.onBindMethod(res);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Client died receiving input method " + args.arg2);
                } finally {
                    // Dispose the channel if the input method is not local to this process
                    // because the remote proxy will get its own copy when unparceled.
                    if (res.channel != null && Binder.isProxy(client)) {
                        res.channel.dispose();
                    }
                }
                args.recycle();
                return true;
            }
            case MSG_BIND_ACCESSIBILITY_SERVICE: {
                args = (SomeArgs) msg.obj;
                IInputMethodClient client = (IInputMethodClient) args.arg1;
                IInputMethodClientInvoker client = (IInputMethodClientInvoker) args.arg1;
                InputBindResult res = (InputBindResult) args.arg2;
                int id = (int) args.arg3;
                try {
                client.onBindAccessibilityService(res, id);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Client died receiving input method " + args.arg2);
                } finally {
                    // Dispose the channel if the accessibility service is not local to this process
                    // because the remote proxy will get its own copy when unparceled.
                    if (res.channel != null && Binder.isProxy(client)) {
                        res.channel.dispose();
                    }
                }
                args.recycle();
                return true;
            }
            case MSG_SET_ACTIVE: {
                args = (SomeArgs) msg.obj;
                final ClientState clientState = (ClientState) args.arg1;
                try {
                clientState.client.setActive(args.argi1 != 0 /* active */,
                        args.argi2 != 0 /* fullScreen */,
                        args.argi3 != 0 /* reportToImeController */);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
                            + clientState.pid + " uid " + clientState.uid);
                }
                args.recycle();
                return true;
            }
@@ -5103,13 +5042,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            case MSG_REPORT_FULLSCREEN_MODE: {
                final boolean fullscreen = msg.arg1 != 0;
                final ClientState clientState = (ClientState)msg.obj;
                try {
                clientState.client.reportFullscreenMode(fullscreen);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Got RemoteException sending "
                            + "reportFullscreen(" + fullscreen + ") notification to pid="
                            + clientState.pid + " uid=" + clientState.uid);
                }
                return true;
            }

@@ -6609,12 +6542,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }
        for (ClientState state : clients.values()) {
            if (state != null) {
                try {
                state.client.setImeTraceEnabled(isImeTraceEnabled);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error while trying to enable/disable ime trace on client window",
                            e);
                }
            }
        }
        return ShellCommandResult.SUCCESS;