Hide IInputMethodManager.{add,remove}Client() from apps

Since IInputMethodManager.{add,remove}Client() are just callbacks from
WindowManagerService to InputMethodManagerService, we can simply move
those two methods to InputMethodManagerInternal instead of having them
in IInputMethodManager.aidl, which ends up exposing those methods to
any random user process.

This enables us to clean up WindowManagerService because it is no
longer responsible for obtaining IInputMethodManager and passing it to
each Session instance.

This also allows us to get rid of several RemoteException handlers
from Session class, because now the complier knows that those
callbacks will never throw RemoteException.

Fix: 112670859
Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases
Test: prebuilts/checkstyle/checkstyle.py -f frameworks/base/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
Change-Id: I453200fd5847e9a78876affb6a1caec221525e1d
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 06726bd..4b004e2 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -43,9 +43,6 @@
     // TODO: We should change the return type from List to List<Parcelable>
     // Currently there is a bug that aidl doesn't accept List<Parcelable>
     List getShortcutInputMethodsAndSubtypes();
-    void addClient(in IInputMethodClient client,
-            in IInputContext inputContext, int uid, int pid);
-    void removeClient(in IInputMethodClient client);
 
     void finishInput(in IInputMethodClient client);
     boolean showSoftInput(in IInputMethodClient client, int flags,
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index e5f4df4..4e4e208 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -204,6 +204,8 @@
     static final int MSG_START_INPUT = 2000;
     static final int MSG_START_VR_INPUT = 2010;
 
+    static final int MSG_ADD_CLIENT = 2980;
+    static final int MSG_REMOVE_CLIENT = 2990;
     static final int MSG_UNBIND_CLIENT = 3000;
     static final int MSG_BIND_CLIENT = 3010;
     static final int MSG_SET_ACTIVE = 3020;
@@ -1712,22 +1714,13 @@
         }
     }
 
-    @Override
-    public void addClient(IInputMethodClient client, IInputContext inputContext, int uid, int pid) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only system process can call this method.");
-        }
+    void addClient(ClientState clientState) {
         synchronized (mMethodMap) {
-            mClients.put(client.asBinder(), new ClientState(client,
-                    inputContext, uid, pid));
+            mClients.put(clientState.client.asBinder(), clientState);
         }
     }
 
-    @Override
-    public void removeClient(IInputMethodClient client) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only system process can call this method.");
-        }
+    void removeClient(IInputMethodClient client) {
         synchronized (mMethodMap) {
             ClientState cs = mClients.remove(client.asBinder());
             if (cs != null) {
@@ -3414,6 +3407,15 @@
             }
 
             // ---------------------------------------------------------
+            case MSG_ADD_CLIENT:
+                addClient((ClientState) msg.obj);
+                return true;
+
+            case MSG_REMOVE_CLIENT:
+                removeClient((IInputMethodClient) msg.obj);
+                return true;
+
+            // ---------------------------------------------------------
 
             case MSG_UNBIND_CLIENT:
                 try {
@@ -4404,7 +4406,7 @@
         }
     }
 
-    private static final class LocalServiceImpl implements InputMethodManagerInternal {
+    private static final class LocalServiceImpl extends InputMethodManagerInternal {
         @NonNull
         private final Handler mHandler;
 
@@ -4413,6 +4415,18 @@
         }
 
         @Override
+        public void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
+                int pid) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_ADD_CLIENT,
+                    new ClientState(client, inputContext, uid, pid)));
+        }
+
+        @Override
+        public void removeClient(IInputMethodClient client) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_CLIENT, client));
+        }
+
+        @Override
         public void setInteractive(boolean interactive) {
             // Do everything in handler so as not to block the caller.
             mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index b2c0aa2..b57356f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,23 +18,76 @@
 
 import android.content.ComponentName;
 
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodClient;
+
 /**
  * Input method manager local system service interface.
  */
-public interface InputMethodManagerInternal {
+public abstract class InputMethodManagerInternal {
+    /**
+     * Called by the window manager service when a client process is being attached to the window
+     * manager service.
+     * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
+     *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
+     *               process
+     * @param inputContext communication channel for the dummy
+     *                     {@link android.view.inputmethod.InputConnection}
+     * @param uid UID of the client process
+     * @param pid PID of the client process
+     */
+    public abstract void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
+            int pid);
+
+    /**
+     * Called by the window manager service when a client process is being attached to the window
+     * manager service.
+     * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
+     *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
+     *               process
+     */
+    public abstract void removeClient(IInputMethodClient client);
+
     /**
      * Called by the power manager to tell the input method manager whether it
      * should start watching for wake events.
      */
-    void setInteractive(boolean interactive);
+    public abstract void setInteractive(boolean interactive);
 
     /**
      * Hides the current input method, if visible.
      */
-    void hideCurrentInputMethod();
+    public abstract void hideCurrentInputMethod();
 
     /**
      * Switches to VR InputMethod defined in the packageName of {@param componentName}.
      */
-    void startVrInputMethodNoCheck(ComponentName componentName);
+    public abstract void startVrInputMethodNoCheck(ComponentName componentName);
+
+    /**
+     * Fake implementation of {@link InputMethodManagerInternal}.  All the methods do nothing.
+     */
+    public static final InputMethodManagerInternal NOP =
+            new InputMethodManagerInternal() {
+                @Override
+                public void addClient(IInputMethodClient client, IInputContext inputContext,
+                        int uid, int pid) {
+                }
+
+                @Override
+                public void removeClient(IInputMethodClient client) {
+                }
+
+                @Override
+                public void setInteractive(boolean interactive) {
+                }
+
+                @Override
+                public void hideCurrentInputMethod() {
+                }
+
+                @Override
+                public void startVrInputMethodNoCheck(ComponentName componentName) {
+                }
+            };
 }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0319864..f9a71d3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -58,7 +58,8 @@
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
-import com.android.internal.view.IInputMethodManager;
+import com.android.server.LocalServices;
+import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
@@ -73,6 +74,7 @@
     final WindowManagerService mService;
     final IWindowSessionCallback mCallback;
     final IInputMethodClient mClient;
+    final InputMethodManagerInternal mInputMethodManagerInternal;
     final int mUid;
     final int mPid;
     private final String mStringName;
@@ -98,6 +100,12 @@
         mService = service;
         mCallback = callback;
         mClient = client;
+        // Depending on the timing when Session object gets called and SystemServer#mFactoryTestMode
+        // this could be null, right?
+        final InputMethodManagerInternal immInternal =
+                LocalServices.getService(InputMethodManagerInternal.class);
+        mInputMethodManagerInternal =
+                immInternal != null ? immInternal : InputMethodManagerInternal.NOP;
         mUid = Binder.getCallingUid();
         mPid = Binder.getCallingPid();
         mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
@@ -126,31 +134,12 @@
         sb.append("}");
         mStringName = sb.toString();
 
-        synchronized (mService.mWindowMap) {
-            if (mService.mInputMethodManager == null && mService.mHaveInputMethods) {
-                IBinder b = ServiceManager.getService(
-                        Context.INPUT_METHOD_SERVICE);
-                mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b);
-            }
-        }
-        long ident = Binder.clearCallingIdentity();
+        mInputMethodManagerInternal.addClient(client, inputContext, mUid, mPid);
         try {
-            // Note: it is safe to call in to the input method manager
-            // here because we are not holding our lock.
-            if (mService.mInputMethodManager != null) {
-                mService.mInputMethodManager.addClient(client, inputContext, mUid, mPid);
-            }
             client.asBinder().linkToDeath(this, 0);
         } catch (RemoteException e) {
             // The caller has died, so we can just forget about this.
-            try {
-                if (mService.mInputMethodManager != null) {
-                    mService.mInputMethodManager.removeClient(client);
-                }
-            } catch (RemoteException ee) {
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInputMethodManagerInternal.removeClient(client);
         }
     }
 
@@ -170,14 +159,7 @@
 
     @Override
     public void binderDied() {
-        // Note: it is safe to call in to the input method manager
-        // here because we are not holding our lock.
-        try {
-            if (mService.mInputMethodManager != null) {
-                mService.mInputMethodManager.removeClient(mClient);
-            }
-        } catch (RemoteException e) {
-        }
+        mInputMethodManagerInternal.removeClient(mClient);
         synchronized(mService.mWindowMap) {
             mClient.asBinder().unlinkToDeath(this, 0);
             mClientDead = true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index da77edf..ea0dd7e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -234,7 +234,6 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
-import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.WindowManagerPolicyThread;
 import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
@@ -417,8 +416,6 @@
 
     final Context mContext;
 
-    final boolean mHaveInputMethods;
-
     final boolean mHasPermanentDpad;
     final long mDrawLockTimeoutMillis;
     final boolean mAllowAnimationsInLowPowerMode;
@@ -521,8 +518,6 @@
     /** List of window currently causing non-system overlay windows to be hidden. */
     private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();
 
-    IInputMethodManager mInputMethodManager;
-
     AccessibilityController mAccessibilityController;
     private RecentsAnimationController mRecentsAnimationController;
 
@@ -905,11 +900,10 @@
     }
 
     public static WindowManagerService main(final Context context, final InputManagerService im,
-            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
-            WindowManagerPolicy policy) {
+            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy) {
         DisplayThread.getHandler().runWithScissors(() ->
-                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
-                        onlyCore, policy), 0);
+                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy),
+                0);
         return sInstance;
     }
 
@@ -930,11 +924,9 @@
     }
 
     private WindowManagerService(Context context, InputManagerService inputManager,
-            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
-            WindowManagerPolicy policy) {
+            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy) {
         installLock(this, INDEX_WINDOW);
         mContext = context;
-        mHaveInputMethods = haveInputMethods;
         mAllowBootMessages = showBootMsgs;
         mOnlyCore = onlyCore;
         mLimitedAlphaCompositing = context.getResources().getBoolean(
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2f6fca4..a61f94c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -888,9 +888,8 @@
             // WMS needs sensor service ready
             ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
             mSensorServiceStart = null;
-            wm = WindowManagerService.main(context, inputManager,
-                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
-                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
+            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
+                    new PhoneWindowManager());
             ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                     DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
             ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index 6664756..54fd7db 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -111,7 +111,7 @@
                     doReturn(input[1]).when(ims).monitorInput(anyString());
                 }
 
-                mService = WindowManagerService.main(context, ims, true, false,
+                mService = WindowManagerService.main(context, ims, false,
                         false, mPolicy = new TestWindowManagerPolicy(
                                 WindowManagerServiceRule.this::getWindowManagerService));