Protect isInputMethodPickerShown() with TEST_INPUT_METHOD permission

IInputMethodManager#isInputMethodPickerShownForTest() was introduced
in Android P (API 28) to verify IME picker visibility in CTS [1].

To make it clear that that IPC method must be available only for
special testing purpose, this CL introduces an @hide permission

   android.permission.TEST_INPUT_METHOD

and requires it in

   InputMethodManagerService#isInputMethodPickerShownForTest().

This CL grants that permission to the shell process hence CTS tests
can still access to the corresponding test API by using
UiAutomation#adoptShellPermissionIdentity().

 [1]: I4e21625c32a0ca1abc740229efb3c7fcd97141cc
      eb5706183f62b9230fb1ae9eb22254a062e7869c

Bug: 237317525
Test: atest CtsInputMethodTestCases
Test: Manually verified as follows.
 1. adb logcat -b events | grep 237317525
 2. atest CtsInputMethodTestCases:InputMethodManagerTest#testIsInputMethodPickerShownProtection
Ignore-AOSP-First: For a security fix
Change-Id: Ie79a3e9d41ce22605ae083594d639c37d08b7def
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 4ddbaa6..718f36f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -81,7 +81,11 @@
             int auxiliarySubtypeMode, int displayId);
 
     void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.TEST_INPUT_METHOD)")
     boolean isInputMethodPickerShownForTest();
+
     InputMethodSubtype getCurrentInputMethodSubtype();
     void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
     // This is kept due to @UnsupportedAppUsage.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c2fcd1d..8180864 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4068,6 +4068,11 @@
     <permission android:name="android.permission.BIND_INPUT_METHOD"
         android:protectionLevel="signature" />
 
+    <!-- Allows access to Test APIs defined in {@link android.view.inputmethod.InputMethodManager}.
+         @hide -->
+    <permission android:name="android.permission.TEST_INPUT_METHOD"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by an {@link android.media.midi.MidiDeviceService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2d384c2..b31e36c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -596,6 +596,9 @@
     <!-- Permission required for CTS test - ClipboardManagerTest -->
     <uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
 
+    <!-- Permission required for CTS test - CtsInputMethodTestCases -->
+    <uses-permission android:name="android.permission.TEST_INPUT_METHOD" />
+
     <!-- Permission required for CTS test - FontManagerTest -->
     <uses-permission android:name="android.permission.UPDATE_FONTS" />
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4886e6e..411bfbe 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -294,6 +294,8 @@
     final IWindowManager mIWindowManager;
     private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid =
             new SparseBooleanArray(0);
+    private final SparseBooleanArray mLoggedDeniedIsInputMethodPickerShownForTestForUid =
+            new SparseBooleanArray(0);
     final WindowManagerInternal mWindowManagerInternal;
     final PackageManagerInternal mPackageManagerInternal;
     final InputManagerInternal mInputManagerInternal;
@@ -1465,6 +1467,7 @@
         public void onUidRemoved(int uid) {
             synchronized (ImfLock.class) {
                 mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.delete(uid);
+                mLoggedDeniedIsInputMethodPickerShownForTestForUid.delete(uid);
             }
         }
 
@@ -4038,6 +4041,18 @@
      * A test API for CTS to make sure that the input method menu is showing.
      */
     public boolean isInputMethodPickerShownForTest() {
+        if (mContext.checkCallingPermission(android.Manifest.permission.TEST_INPUT_METHOD)
+                != PackageManager.PERMISSION_GRANTED) {
+            final int callingUid = Binder.getCallingUid();
+            synchronized (ImfLock.class) {
+                if (!mLoggedDeniedIsInputMethodPickerShownForTestForUid.get(callingUid)) {
+                    EventLog.writeEvent(0x534e4554, "237317525", callingUid, "");
+                    mLoggedDeniedIsInputMethodPickerShownForTestForUid.put(callingUid, true);
+                }
+            }
+            throw new SecurityException(
+                    "isInputMethodPickerShownForTest requires TEST_INPUT_METHOD permission");
+        }
         synchronized (ImfLock.class) {
             return mMenuController.isisInputMethodPickerShownForTestLocked();
         }