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

Commit 3422879e authored by Rhed Jao's avatar Rhed Jao Committed by Android (Google) Code Review
Browse files

Merge "Package visibility improvement for the InputMethodManager"

parents d4c74dd7 28f26513
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -267,6 +267,12 @@ import java.util.function.Consumer;
 * they can switch to it, to confirm with the system that they know about it
 * and want to make it available for use.</p>
 * </ul>
 *
 * <p>If your app targets Android 11 (API level 30) or higher, the methods in
 * this class each return a filtered result by the rules of
 * <a href="/training/basics/intents/package-visibility">package visibility</a>,
 * except for the currently connected IME. Apps having a query for the
 * {@link InputMethod#SERVICE_INTERFACE} see all IMEs.</p>
 */
@SystemService(Context.INPUT_METHOD_SERVICE)
@RequiresFeature(PackageManager.FEATURE_INPUT_METHODS)
+68 −16
Original line number Diff line number Diff line
@@ -2036,9 +2036,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            if (resolvedUserIds.length != 1) {
                return Collections.emptyList();
            }
            final int callingUid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                return getInputMethodListLocked(resolvedUserIds[0], directBootAwareness);
                return getInputMethodListLocked(
                        resolvedUserIds[0], directBootAwareness, callingUid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -2057,9 +2059,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            if (resolvedUserIds.length != 1) {
                return Collections.emptyList();
            }
            final int callingUid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                return getEnabledInputMethodListLocked(resolvedUserIds[0]);
                return getEnabledInputMethodListLocked(resolvedUserIds[0], callingUid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -2090,12 +2093,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

    @GuardedBy("ImfLock.class")
    private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId,
            @DirectBootAwareness int directBootAwareness) {
            @DirectBootAwareness int directBootAwareness, int callingUid) {
        final ArrayList<InputMethodInfo> methodList;
        final InputMethodSettings settings;
        if (userId == mSettings.getCurrentUserId()
                && directBootAwareness == DirectBootAwareness.AUTO) {
            // Create a copy.
            methodList = new ArrayList<>(mMethodList);
            settings = mSettings;
        } else {
            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
            methodList = new ArrayList<>();
@@ -2104,19 +2109,31 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
                    methodList, directBootAwareness);
            settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
        }
        // filter caller's access to input methods
        methodList.removeIf(imi ->
                !canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings));
        return methodList;
    }

    @GuardedBy("ImfLock.class")
    private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
    private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId,
            int callingUid) {
        final ArrayList<InputMethodInfo> methodList;
        final InputMethodSettings settings;
        if (userId == mSettings.getCurrentUserId()) {
            return mSettings.getEnabledInputMethodListLocked();
        }
            methodList = mSettings.getEnabledInputMethodListLocked();
            settings = mSettings;
        } else {
            final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
        final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId,
                true);
        return settings.getEnabledInputMethodListLocked();
            settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
            methodList = settings.getEnabledInputMethodListLocked();
        }
        // filter caller's access to input methods
        methodList.removeIf(imi ->
                !canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings));
        return methodList;
    }

    @GuardedBy("ImfLock.class")
@@ -2155,10 +2172,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }

        synchronized (ImfLock.class) {
            final int callingUid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                return getEnabledInputMethodSubtypeListLocked(imiId,
                        allowsImplicitlyEnabledSubtypes, userId);
                        allowsImplicitlyEnabledSubtypes, userId, callingUid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -2167,7 +2185,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

    @GuardedBy("ImfLock.class")
    private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
            boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
            boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId, int callingUid) {
        if (userId == mSettings.getCurrentUserId()) {
            final InputMethodInfo imi;
            String selectedMethodId = getSelectedMethodIdLocked();
@@ -2176,7 +2194,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            } else {
                imi = mMethodMap.get(imiId);
            }
            if (imi == null) {
            if (imi == null || !canCallerAccessInputMethod(
                    imi.getPackageName(), callingUid, userId, mSettings)) {
                return Collections.emptyList();
            }
            return mSettings.getEnabledInputMethodSubtypeListLocked(
@@ -2189,6 +2208,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }
        final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId,
                true);
        if (!canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings)) {
            return Collections.emptyList();
        }
        return settings.getEnabledInputMethodSubtypeListLocked(
                imi, allowsImplicitlyEnabledSubtypes);
    }
@@ -5438,6 +5460,34 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        return true;
    }

    /**
     * Filter the access to the input method by rules of the package visibility. Return {@code true}
     * if the given input method is the currently selected one or visible to the caller.
     *
     * @param targetPkgName The package name of input method to check.
     * @param callingUid The caller that is going to access the input method.
     * @param userId The user ID where the input method resides.
     * @param settings The input method settings under the given user ID.
     * @return {@code true} if caller is able to access the input method.
     */
    private boolean canCallerAccessInputMethod(@NonNull String targetPkgName, int callingUid,
            @UserIdInt int userId, @NonNull InputMethodSettings settings) {
        final String methodId = settings.getSelectedInputMethod();
        final ComponentName selectedInputMethod = methodId != null
                ? InputMethodUtils.convertIdToComponentName(methodId) : null;
        if (selectedInputMethod != null
                && selectedInputMethod.getPackageName().equals(targetPkgName)) {
            return true;
        }
        final boolean canAccess = !mPackageManagerInternal.filterAppAccess(
                targetPkgName, callingUid, userId);
        if (DEBUG && !canAccess) {
            Slog.d(TAG, "Input method " + targetPkgName
                    + " is not visible to the caller " + callingUid);
        }
        return canAccess;
    }

    private void publishLocalService() {
        LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl());
    }
@@ -5459,14 +5509,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        @Override
        public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
            synchronized (ImfLock.class) {
                return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
                return getInputMethodListLocked(userId, DirectBootAwareness.AUTO,
                        Process.SYSTEM_UID);
            }
        }

        @Override
        public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
            synchronized (ImfLock.class) {
                return getEnabledInputMethodListLocked(userId);
                return getEnabledInputMethodListLocked(userId, Process.SYSTEM_UID);
            }
        }

@@ -6096,8 +6147,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            try (PrintWriter pr = shellCommand.getOutPrintWriter()) {
                for (int userId : userIds) {
                    final List<InputMethodInfo> methods = all
                            ? getInputMethodListLocked(userId, DirectBootAwareness.AUTO)
                            : getEnabledInputMethodListLocked(userId);
                            ? getInputMethodListLocked(
                                    userId, DirectBootAwareness.AUTO, Process.SHELL_UID)
                            : getEnabledInputMethodListLocked(userId, Process.SHELL_UID);
                    if (userIds.length > 1) {
                        pr.print("User #");
                        pr.print(userId);
+14 −0
Original line number Diff line number Diff line
@@ -20,11 +20,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Build;
import android.os.UserHandle;
@@ -998,4 +1000,16 @@ final class InputMethodUtils {
        }
        return new int[]{sourceUserId};
    }

    /**
     * Convert the input method ID to a component name
     *
     * @param id A unique ID for this input method.
     * @return The component name of the input method.
     * @see InputMethodInfo#computeId(ResolveInfo)
     */
    @Nullable
    public static ComponentName convertIdToComponentName(@NonNull String id) {
        return ComponentName.unflattenFromString(id);
    }
}