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

Commit a6a20724 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Enable background users to call IMM#getCurrentInputMethodSubtype()

This is a follow up CL to my previous CLs [1][2], which let query APIs
defined in InputMethodManager support background users.

With this CL,

  InputMethodManager#getCurrentInputMethodSubtype()

is also fully supported under multi-user / multi-profile environment.

 [1]: I192a0f5a1375170d17a4c08af94f23966dbaea8b
      7f8ee4b9
 [2]: I48f57dc7184e85bdb422fd9d1d56e60381654125
      e9609865

Bug: 34886274
Bug: 122164939
Bug: 237316307
Test: atest CtsInputMethodTestCases:InputMethodSubtypeTest
Change-Id: Icb09f9cb1a4147884faa9952b2e03ec4afa9f0b1
parent 2cc1eee4
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -182,9 +182,9 @@ final class IInputMethodManagerInvoker {

    @AnyThread
    @Nullable
    InputMethodSubtype getCurrentInputMethodSubtype() {
    InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
        try {
            return mTarget.getCurrentInputMethodSubtype();
            return mTarget.getCurrentInputMethodSubtype(userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+1 −1
Original line number Diff line number Diff line
@@ -3374,7 +3374,7 @@ public final class InputMethodManager {
     */
    @Nullable
    public InputMethodSubtype getCurrentInputMethodSubtype() {
        return mServiceInvoker.getCurrentInputMethodSubtype();
        return mServiceInvoker.getCurrentInputMethodSubtype(UserHandle.myUserId());
    }

    /**
+4 −1
Original line number Diff line number Diff line
@@ -88,7 +88,10 @@ interface IInputMethodManager {
            + "android.Manifest.permission.TEST_INPUT_METHOD)")
    boolean isInputMethodPickerShownForTest();

    @nullable InputMethodSubtype getCurrentInputMethodSubtype();
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
    @nullable InputMethodSubtype getCurrentInputMethodSubtype(int userId);

    void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
    // This is kept due to @UnsupportedAppUsage.
    // TODO(Bug 113914148): Consider removing this.
+25 −5
Original line number Diff line number Diff line
@@ -5284,18 +5284,38 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

    /**
     * Gets the current subtype of this input method.
     *
     * @param userId User ID to be queried about.
     * @return The current {@link InputMethodSubtype} for the specified user.
     */
    @Nullable
    @Override
    public InputMethodSubtype getCurrentInputMethodSubtype() {
        synchronized (ImfLock.class) {
            // TODO: Make this work even for non-current users?
            if (!calledFromValidUserLocked()) {
                return null;
    public InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
        if (UserHandle.getCallingUserId() != userId) {
            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
        }
        synchronized (ImfLock.class) {
            if (mSettings.getCurrentUserId() == userId) {
                return getCurrentInputMethodSubtypeLocked();
            }

            final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
            final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap,
                    userId, false);
            return settings.getCurrentInputMethodSubtypeForNonCurrentUsers();
        }
    }

    /**
     * Returns the current {@link InputMethodSubtype} for the current user.
     *
     * <p>CAVEATS: You must also update
     * {@link InputMethodSettings#getCurrentInputMethodSubtypeForNonCurrentUsers()}
     * when you update the algorithm of this method.</p>
     *
     * <p>TODO: Address code duplication between this and
     * {@link InputMethodSettings#getCurrentInputMethodSubtypeForNonCurrentUsers()}.</p>
     */
    @GuardedBy("ImfLock.class")
    InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
        String selectedMethodId = getSelectedMethodIdLocked();
+50 −0
Original line number Diff line number Diff line
@@ -818,6 +818,56 @@ final class InputMethodUtils {
            }
        }

        /**
         * A variant of {@link InputMethodManagerService#getCurrentInputMethodSubtypeLocked()} for
         * non-current users.
         *
         * <p>TODO: Address code duplication between this and
         * {@link InputMethodManagerService#getCurrentInputMethodSubtypeLocked()}.</p>
         *
         * @return {@link InputMethodSubtype} if exists. {@code null} otherwise.
         */
        @Nullable
        InputMethodSubtype getCurrentInputMethodSubtypeForNonCurrentUsers() {
            final String selectedMethodId = getSelectedInputMethod();
            if (selectedMethodId == null) {
                return null;
            }
            final InputMethodInfo imi = mMethodMap.get(selectedMethodId);
            if (imi == null || imi.getSubtypeCount() == 0) {
                return null;
            }

            final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
            if (subtypeHashCode != InputMethodUtils.NOT_A_SUBTYPE_ID) {
                final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi,
                        subtypeHashCode);
                if (subtypeIndex >= 0) {
                    return imi.getSubtypeAt(subtypeIndex);
                }
            }

            // If there are no selected subtypes, the framework will try to find the most applicable
            // subtype from explicitly or implicitly enabled subtypes.
            final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
                    getEnabledInputMethodSubtypeListLocked(imi, true);
            // If there is only one explicitly or implicitly enabled subtype, just returns it.
            if (explicitlyOrImplicitlyEnabledSubtypes.isEmpty()) {
                return null;
            }
            if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
                return explicitlyOrImplicitlyEnabledSubtypes.get(0);
            }
            final InputMethodSubtype subtype = SubtypeUtils.findLastResortApplicableSubtypeLocked(
                    mRes, explicitlyOrImplicitlyEnabledSubtypes, SubtypeUtils.SUBTYPE_MODE_KEYBOARD,
                    null, true);
            if (subtype != null) {
                return subtype;
            }
            return SubtypeUtils.findLastResortApplicableSubtypeLocked(mRes,
                    explicitlyOrImplicitlyEnabledSubtypes, null, null, true);
        }

        public void dumpLocked(final Printer pw, final String prefix) {
            pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
            pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds));