Loading core/java/android/view/inputmethod/InputMethodManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -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) Loading services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +68 −16 Original line number Diff line number Diff line Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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<>(); Loading @@ -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") Loading Loading @@ -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); } Loading @@ -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(); Loading @@ -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( Loading @@ -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); } Loading Loading @@ -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()); } Loading @@ -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); } } Loading Loading @@ -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); Loading services/core/java/com/android/server/inputmethod/InputMethodUtils.java +14 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } Loading
core/java/android/view/inputmethod/InputMethodManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -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) Loading
services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +68 −16 Original line number Diff line number Diff line Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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<>(); Loading @@ -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") Loading Loading @@ -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); } Loading @@ -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(); Loading @@ -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( Loading @@ -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); } Loading Loading @@ -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()); } Loading @@ -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); } } Loading Loading @@ -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); Loading
services/core/java/com/android/server/inputmethod/InputMethodUtils.java +14 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } }