diff options
| author | Adam Bookatz <bookatz@google.com> | 2022-02-04 17:11:25 -0800 |
|---|---|---|
| committer | Adam Bookatz <bookatz@google.com> | 2022-02-10 13:17:28 -0800 |
| commit | c9bc9adfd7da542e1275daecec3c965725fd6cd4 (patch) | |
| tree | 932cb1e0e741d1267f0ea5509a6832f0c8b0972f /core/java/android/os/UserManager.java | |
| parent | b2134e69f7e12ef0c90574950a7fd5d3301469e3 (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.java | 126 |
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. |
