Merge "Package visibility improvement for the InputMethodManager"
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
|
||||
methodList = settings.getEnabledInputMethodListLocked();
|
||||
}
|
||||
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
|
||||
final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId,
|
||||
true);
|
||||
return 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user