diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c3c53673aa159..0486d4c9b3a1c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9642,7 +9642,7 @@ package android.os { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser(); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile(); + method public boolean isCloneProfile(); method public boolean isCredentialSharedWithParent(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int); diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index fcce266d6a2d4..39ca596f9181e 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -107,9 +107,7 @@ interface IUserManager { void clearSeedAccountData(int userId); boolean someUserHasSeedAccount(in String accountName, in String accountType); boolean someUserHasAccount(in String accountName, in String accountType); - boolean isProfile(int userId); - boolean isManagedProfile(int userId); - boolean isCloneProfile(int userId); + String getProfileType(int userId); boolean isMediaSharedWithParent(int userId); boolean isCredentialSharedWithParent(int userId); boolean isDemoUser(int userId); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index c597a1a6e7dc3..886c3fcb05f5f 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -98,8 +98,8 @@ public class UserManager { /** The userId of the constructor param context. To be used instead of mContext.getUserId(). */ private final @UserIdInt int mUserId; - private Boolean mIsManagedProfileCached; - private Boolean mIsProfileCached; + /** The userType of UserHandle.myUserId(); empty string if not a profile; null until cached. */ + private String mProfileTypeOfProcessUser = null; /** * User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user. @@ -2276,7 +2276,7 @@ public class UserManager { * {@link UserManager#USER_TYPE_PROFILE_MANAGED managed profile}. * @hide */ - public static boolean isUserTypeManagedProfile(String userType) { + public static boolean isUserTypeManagedProfile(@Nullable String userType) { return USER_TYPE_PROFILE_MANAGED.equals(userType); } @@ -2284,7 +2284,7 @@ public class UserManager { * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_GUEST guest user}. * @hide */ - public static boolean isUserTypeGuest(String userType) { + public static boolean isUserTypeGuest(@Nullable String userType) { return USER_TYPE_FULL_GUEST.equals(userType); } @@ -2293,7 +2293,7 @@ public class UserManager { * {@link UserManager#USER_TYPE_FULL_RESTRICTED restricted user}. * @hide */ - public static boolean isUserTypeRestricted(String userType) { + public static boolean isUserTypeRestricted(@Nullable String userType) { return USER_TYPE_FULL_RESTRICTED.equals(userType); } @@ -2301,7 +2301,7 @@ public class UserManager { * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_DEMO demo user}. * @hide */ - public static boolean isUserTypeDemo(String userType) { + public static boolean isUserTypeDemo(@Nullable String userType) { return USER_TYPE_FULL_DEMO.equals(userType); } @@ -2309,7 +2309,7 @@ public class UserManager { * Returns whether the user type is a {@link UserManager#USER_TYPE_PROFILE_CLONE clone user}. * @hide */ - public static boolean isUserTypeCloneProfile(String userType) { + public static boolean isUserTypeCloneProfile(@Nullable String userType) { return USER_TYPE_PROFILE_CLONE.equals(userType); } @@ -2525,25 +2525,50 @@ public class UserManager { } private boolean isProfile(@UserIdInt int userId) { - if (userId == mUserId) { + final String profileType = getProfileType(userId); + return profileType != null && !profileType.equals(""); + } + + /** + * Returns the user type of the context user if it is a profile. + * + * This is a more specific form of {@link #getUserType()} with relaxed permission requirements. + * + * @return the user type of the context user if it is a {@link #isProfile() profile}, + * an empty string if it is not a profile, + * or null if the user doesn't exist. + */ + @UserHandleAware( + requiresAnyOfPermissionsIfNotCallerProfileGroup = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.QUERY_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + private @Nullable String getProfileType() { + return getProfileType(mUserId); + } + + /** @see #getProfileType() */ + private @Nullable String getProfileType(@UserIdInt int userId) { + // First, the typical case (i.e. the *process* user, not necessarily the context user). + // This cache cannot be become invalidated since it's about the calling process itself. + if (userId == UserHandle.myUserId()) { // No need for synchronization. Once it becomes non-null, it'll be non-null forever. // Worst case we might end up calling the AIDL method multiple times but that's fine. - if (mIsProfileCached != null) { - return mIsProfileCached; + if (mProfileTypeOfProcessUser != null) { + return mProfileTypeOfProcessUser; } try { - mIsProfileCached = mService.isProfile(mUserId); - return mIsProfileCached; - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } else { - try { - return mService.isProfile(userId); + final String profileType = mService.getProfileType(userId); + if (profileType != null) { + return mProfileTypeOfProcessUser = profileType.intern(); + } } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } + + // The userId is not for the process's user. Use a slower cache that handles invalidation. + return mProfileTypeCache.query(userId); } /** @@ -2577,50 +2602,26 @@ public class UserManager { android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public boolean isManagedProfile(@UserIdInt int userId) { - if (userId == mUserId) { - // No need for synchronization. Once it becomes non-null, it'll be non-null forever. - // Worst case we might end up calling the AIDL method multiple times but that's fine. - if (mIsManagedProfileCached != null) { - return mIsManagedProfileCached; - } - try { - mIsManagedProfileCached = mService.isManagedProfile(mUserId); - return mIsManagedProfileCached; - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } else { - try { - return mService.isManagedProfile(userId); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } + return isUserTypeManagedProfile(getProfileType(userId)); } /** * Checks if the context user is a clone profile. * - *
Requires {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
- * must be in the same profile group of the user.
- *
* @return whether the context user is a clone profile.
*
* @see android.os.UserManager#USER_TYPE_PROFILE_CLONE
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
- @UserHandleAware
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
@SuppressAutoDoc
public boolean isCloneProfile() {
- try {
- return mService.isCloneProfile(mUserId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return isUserTypeCloneProfile(getProfileType());
}
/**
@@ -5247,6 +5248,33 @@ public class UserManager {
}
}
+ /* Cache key for anything that assumes that userIds cannot be re-used without rebooting. */
+ private static final String CACHE_KEY_STATIC_USER_PROPERTIES = "cache_key.static_user_props";
+
+ private final PropertyInvalidatedCache