Loading core/api/system-current.txt +1 −1 Original line number Diff line number Diff line Loading @@ -9710,7 +9710,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); core/java/android/os/IUserManager.aidl +1 −3 Original line number Diff line number Diff line Loading @@ -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); Loading core/java/android/os/UserManager.java +77 −49 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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(); final String profileType = mService.getProfileType(userId); if (profileType != null) { return mProfileTypeOfProcessUser = profileType.intern(); } } else { try { return mService.isProfile(userId); } 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); } /** Loading Loading @@ -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()); } /** Loading Loading @@ -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. Loading services/core/java/com/android/server/pm/UserManagerService.java +12 −13 Original line number Diff line number Diff line Loading @@ -1517,7 +1517,6 @@ public class UserManagerService extends IUserManager.Stub { return userTypeDetails.getBadgeNoBackground(); } @Override public boolean isProfile(@UserIdInt int userId) { checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile"); synchronized (mUsersLock) { Loading @@ -1526,21 +1525,19 @@ public class UserManagerService extends IUserManager.Stub { } } /** * Returns the user type (if it is a profile), empty string (if it isn't a profile), * or null (if the user doesn't exist). */ @Override public boolean isManagedProfile(@UserIdInt int userId) { checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile"); public @Nullable String getProfileType(@UserIdInt int userId) { checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getProfileType"); synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); return userInfo != null && userInfo.isManagedProfile(); if (userInfo != null) { return userInfo.isProfile() ? userInfo.userType : ""; } } @Override public boolean isCloneProfile(@UserIdInt int userId) { checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isCloneProfile"); synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); return userInfo != null && userInfo.isCloneProfile(); return null; } } Loading Loading @@ -5163,6 +5160,8 @@ public class UserManagerService extends IUserManager.Stub { nextId = scanNextAvailableIdLocked(); } } // If we got here, we probably recycled user ids, so invalidate any caches. UserManager.invalidateStaticUserProperties(); if (nextId < 0) { throw new IllegalStateException("No user id available!"); } Loading services/core/java/com/android/server/pm/UserTypeFactory.java +2 −2 Original line number Diff line number Diff line Loading @@ -332,13 +332,13 @@ public final class UserTypeFactory { } String typeName = parser.getAttributeValue(null, "name"); if (typeName == null) { if (typeName == null || typeName.equals("")) { Slog.w(LOG_TAG, "Skipping user type with no name in " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } typeName.intern(); typeName = typeName.intern(); UserTypeDetails.Builder builder; if (typeName.startsWith("android.")) { Loading Loading
core/api/system-current.txt +1 −1 Original line number Diff line number Diff line Loading @@ -9710,7 +9710,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);
core/java/android/os/IUserManager.aidl +1 −3 Original line number Diff line number Diff line Loading @@ -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); Loading
core/java/android/os/UserManager.java +77 −49 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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(); final String profileType = mService.getProfileType(userId); if (profileType != null) { return mProfileTypeOfProcessUser = profileType.intern(); } } else { try { return mService.isProfile(userId); } 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); } /** Loading Loading @@ -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()); } /** Loading Loading @@ -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. Loading
services/core/java/com/android/server/pm/UserManagerService.java +12 −13 Original line number Diff line number Diff line Loading @@ -1517,7 +1517,6 @@ public class UserManagerService extends IUserManager.Stub { return userTypeDetails.getBadgeNoBackground(); } @Override public boolean isProfile(@UserIdInt int userId) { checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile"); synchronized (mUsersLock) { Loading @@ -1526,21 +1525,19 @@ public class UserManagerService extends IUserManager.Stub { } } /** * Returns the user type (if it is a profile), empty string (if it isn't a profile), * or null (if the user doesn't exist). */ @Override public boolean isManagedProfile(@UserIdInt int userId) { checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile"); public @Nullable String getProfileType(@UserIdInt int userId) { checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getProfileType"); synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); return userInfo != null && userInfo.isManagedProfile(); if (userInfo != null) { return userInfo.isProfile() ? userInfo.userType : ""; } } @Override public boolean isCloneProfile(@UserIdInt int userId) { checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isCloneProfile"); synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); return userInfo != null && userInfo.isCloneProfile(); return null; } } Loading Loading @@ -5163,6 +5160,8 @@ public class UserManagerService extends IUserManager.Stub { nextId = scanNextAvailableIdLocked(); } } // If we got here, we probably recycled user ids, so invalidate any caches. UserManager.invalidateStaticUserProperties(); if (nextId < 0) { throw new IllegalStateException("No user id available!"); } Loading
services/core/java/com/android/server/pm/UserTypeFactory.java +2 −2 Original line number Diff line number Diff line Loading @@ -332,13 +332,13 @@ public final class UserTypeFactory { } String typeName = parser.getAttributeValue(null, "name"); if (typeName == null) { if (typeName == null || typeName.equals("")) { Slog.w(LOG_TAG, "Skipping user type with no name in " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } typeName.intern(); typeName = typeName.intern(); UserTypeDetails.Builder builder; if (typeName.startsWith("android.")) { Loading