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

Commit 6c984cca authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Fix a user ID mismatch in IMMS#queryInputMethodServicesInternal

This is a follow up CL to our previous CL [1], which aimed to
introduce a safeguard against Binder transaction failure due to too
large payload, which is actually not something new when dealing with
InputMethodInfo [2].

Anyway, the way how the original CL attempted is to filter out APKs
that contain too many InputMethodService entries (20 entries), unless
the InputMethodService is pre-installed or already enabled, and it was
implemented as an additional step in

  InputMethodManagerService#queryInputMethodServicesInternal().

The primary problem is that to get the list of enabled IME IDs, the
original CL used

  InputMethodManagerService#mSettings.getEnabledInputMethodNames(),

which is valid only for the current IME user. If the query is about
another user, then we are using a wrong user's enabled IME IDs, which
is not our intention. There is, however, a secondary problem on how to
get another user's InputMethodSettings.  Here is why.

Here is the boilerplate code to instantiate an InputMethodSettings
object for a given "userId".

  ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
  ArrayList<InputMethodInfo> methodList = new ArrayList<>();
  ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
          new ArrayMap<>();
  AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
  queryInputMethodServicesInternal(mContext, userId,
          additionalSubtypeMap, methodMap, methodList,
          directBootAwareness);
  InputMethodSettings settings = new InputMethodSettings(mContext,
          methodMap, userId, true /* copyOnWrite */);

With the commit in question [1], however, there is a cyclic data
dependency, that is, we need to rely on

  InputMethodSettings#getEnabledInputMethodNames()

to instantiate an InputMethodSettings.

  ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
  ArrayList<InputMethodInfo> methodList = new ArrayList<>();
  ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
          new ArrayMap<>();
  AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
  queryInputMethodServicesInternal(mContext, userId,
          additionalSubtypeMap, methodMap, methodList,
          directBootAwareness,
          // the following settings is not yet available.
          // it will be created in the next line!!!
          settings.getEnabledInputMethodNames());
  InputMethodSettings settings = new InputMethodSettings(mContext,
          methodMap, userId, true /* copyOnWrite */);

Fortunately, the real data dependency is not cyclic and we can still
create the necessary list by directly accessing

  Settings.Secure.ENABLED_INPUT_METHODS.

With above, this CL introduces

  InputMethodUtils#getEnabledInputMethodIdsForFiltering()

so that queryInputMethodServicesInternal() can be used without relying
on InputMethodSettings.

This CL is also a preparatoin to start maintaining multiple instances
of InputMethodSettings at the same time.

There must be no semantic change in this CL except for we now take
right user's enabled IME IDs into considerations anyway.

 [1]: I51703563414192ee778f30ab57390da1c1a5ded5
      eed10082
 [2]: Ibb2940fcc02f3b3b51ba6bbe127d646fd7de7c45
      f0656956

Bug: 261723412
Bug: 309837937
Test: presubmit
Change-Id: Iac98fdbb2758f4d9c68930f49d219eb83e54a3d0
parent 5d46b5c6
Loading
Loading
Loading
Loading
+11 −9
Original line number Original line Diff line number Diff line
@@ -2085,7 +2085,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    new ArrayMap<>();
                    new ArrayMap<>();
            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
                    methodList, directBootAwareness, mSettings.getEnabledInputMethodNames());
                    methodList, directBootAwareness);
            settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
            settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
        }
        }
        // filter caller's access to input methods
        // filter caller's access to input methods
@@ -4200,7 +4200,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    new ArrayMap<>();
                    new ArrayMap<>();
            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
                    methodList, DirectBootAwareness.AUTO, mSettings.getEnabledInputMethodNames());
                    methodList, DirectBootAwareness.AUTO);
            final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap,
            final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap,
                    userId, false);
                    userId, false);
            settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap,
            settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap,
@@ -5025,7 +5025,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    static void queryInputMethodServicesInternal(Context context,
    static void queryInputMethodServicesInternal(Context context,
            @UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
            @UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
            ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
            ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
            @DirectBootAwareness int directBootAwareness, List<String> enabledInputMethodList) {
            @DirectBootAwareness int directBootAwareness) {
        final Context userAwareContext = context.getUserId() == userId
        final Context userAwareContext = context.getUserId() == userId
                ? context
                ? context
                : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
                : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
@@ -5058,6 +5058,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        methodList.ensureCapacity(services.size());
        methodList.ensureCapacity(services.size());
        methodMap.ensureCapacity(services.size());
        methodMap.ensureCapacity(services.size());


        // Note: This is a temporary solution for Bug 261723412.  If there is any better solution,
        // we should remove this data dependency.
        final List<String> enabledInputMethodList =
                InputMethodUtils.getEnabledInputMethodIdsForFiltering(context, userId);

        filterInputMethodServices(additionalSubtypeMap, methodMap, methodList,
        filterInputMethodServices(additionalSubtypeMap, methodMap, methodList,
                enabledInputMethodList, userAwareContext, services);
                enabledInputMethodList, userAwareContext, services);
    }
    }
@@ -5124,8 +5129,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        mMyPackageMonitor.clearKnownImePackageNamesLocked();
        mMyPackageMonitor.clearKnownImePackageNamesLocked();


        queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(),
        queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(),
                mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO,
                mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO);
                mSettings.getEnabledInputMethodNames());


        // Construct the set of possible IME packages for onPackageChanged() to avoid false
        // Construct the set of possible IME packages for onPackageChanged() to avoid false
        // negatives when the package state remains to be the same but only the component state is
        // negatives when the package state remains to be the same but only the component state is
@@ -5486,8 +5490,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                new ArrayMap<>();
                new ArrayMap<>();
        AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
        AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
        queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
        queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
                methodMap, methodList, DirectBootAwareness.AUTO,
                methodMap, methodList, DirectBootAwareness.AUTO);
                mSettings.getEnabledInputMethodNames());
        return methodMap;
        return methodMap;
    }
    }


@@ -6511,8 +6514,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                                new ArrayMap<>();
                                new ArrayMap<>();
                        AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
                        AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
                        queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
                        queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
                                methodMap, methodList, DirectBootAwareness.AUTO,
                                methodMap, methodList, DirectBootAwareness.AUTO);
                                mSettings.getEnabledInputMethodNames());
                        final InputMethodSettings settings = new InputMethodSettings(mContext,
                        final InputMethodSettings settings = new InputMethodSettings(mContext,
                                methodMap, userId, false);
                                methodMap, userId, false);


+38 −0
Original line number Original line Diff line number Diff line
@@ -1028,6 +1028,44 @@ final class InputMethodUtils {
        return new int[]{sourceUserId};
        return new int[]{sourceUserId};
    }
    }


    /**
     * Returns a list of enabled IME IDs to address Bug 261723412.
     *
     * <p>This is a temporary workaround until we come up with a better solution. Do not use this
     * for anything other than Bug 261723412.</p>
     *
     * @param context {@link Context} object to query secure settings.
     * @param userId User ID to query about.
     * @return A list of enabled IME IDs.
     */
    @NonNull
    static List<String> getEnabledInputMethodIdsForFiltering(@NonNull Context context,
            @UserIdInt int userId) {
        final String enabledInputMethodsStr = TextUtils.nullIfEmpty(
                Settings.Secure.getStringForUser(
                        context.getContentResolver(),
                        Settings.Secure.ENABLED_INPUT_METHODS,
                        userId));
        if (enabledInputMethodsStr == null) {
            return List.of();
        }
        final TextUtils.SimpleStringSplitter inputMethodSplitter =
                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
        final TextUtils.SimpleStringSplitter subtypeSplitter =
                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
        inputMethodSplitter.setString(enabledInputMethodsStr);
        final ArrayList<String> result = new ArrayList<>();
        while (inputMethodSplitter.hasNext()) {
            String nextImsStr = inputMethodSplitter.next();
            subtypeSplitter.setString(nextImsStr);
            if (subtypeSplitter.hasNext()) {
                // The first element is ime id.
                result.add(subtypeSplitter.next());
            }
        }
        return result;
    }

    /**
    /**
     * Convert the input method ID to a component name
     * Convert the input method ID to a component name
     *
     *