summaryrefslogtreecommitdiff
path: root/core/java/android/os/UserManager.java
diff options
context:
space:
mode:
authorAdam Bookatz <bookatz@google.com>2022-02-04 17:11:25 -0800
committerAdam Bookatz <bookatz@google.com>2022-02-10 13:17:28 -0800
commitc9bc9adfd7da542e1275daecec3c965725fd6cd4 (patch)
tree932cb1e0e741d1267f0ea5509a6832f0c8b0972f /core/java/android/os/UserManager.java
parentb2134e69f7e12ef0c90574950a7fd5d3301469e3 (diff)
UserManager.getProfileType hidden API and caching
Currently we have a few APIs for isXXXProfile. In the case of managed profiles, we cachce the results. Here, we introduce a new hidden API getProfileType, which gets and caches the user type (string), if the user type is a profile. That way, we automatically have the caching benefits for all profiles, not just managed profiles. We only do this for profiles because they have a relaxed permission requirement. Other user types have strong restrictions, so we do not use their more general getters here. Test: atest UserManagerTest Change-Id: I36b4caead83954518d6fc48860af61f8988f19a9
Diffstat (limited to 'core/java/android/os/UserManager.java')
-rw-r--r--core/java/android/os/UserManager.java126
1 files changed, 77 insertions, 49 deletions
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c597a1a6e7dc..886c3fcb05f5 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;
- }
- try {
- mIsProfileCached = mService.isProfile(mUserId);
- return mIsProfileCached;
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ if (mProfileTypeOfProcessUser != null) {
+ return mProfileTypeOfProcessUser;
}
- } 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.
*
- * <p>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<Integer, String> mProfileTypeCache =
+ new PropertyInvalidatedCache<Integer, String>(32, CACHE_KEY_STATIC_USER_PROPERTIES) {
+ @Override
+ public String recompute(Integer query) {
+ try {
+ // Will be null (and not cached) if invalid user; otherwise cache the type.
+ String profileType = mService.getProfileType(query);
+ if (profileType != null) profileType = profileType.intern();
+ return profileType;
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ @Override
+ public boolean bypass(Integer query) {
+ return query < 0;
+ }
+ };
+
+ /** {@hide} */
+ public static final void invalidateStaticUserProperties() {
+ PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES);
+ }
+
/**
* @hide
* User that enforces a restriction.