Merge "UserManager.getProfileType hidden API and caching"
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <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.
|
||||
|
||||
@@ -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) {
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloneProfile(@UserIdInt int userId) {
|
||||
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isCloneProfile");
|
||||
synchronized (mUsersLock) {
|
||||
UserInfo userInfo = getUserInfoLU(userId);
|
||||
return userInfo != null && userInfo.isCloneProfile();
|
||||
if (userInfo != null) {
|
||||
return userInfo.isProfile() ? userInfo.userType : "";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
@@ -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.")) {
|
||||
|
||||
Reference in New Issue
Block a user