diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4eeb42f70f35c..92d507a0e3a21 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9969,9 +9969,10 @@ package android.os { method public boolean isCloneProfile(); method public boolean isCredentialSharableWithParent(); 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.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isMainUser(); 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); method public boolean isMediaSharedWithParent(); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isPrimaryUser(); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isPrimaryUser(); method public static boolean isRemoveResultSuccessful(int); method public boolean isRestrictedProfile(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean isRestrictedProfile(@NonNull android.os.UserHandle); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d1c1a17558d4b..3fee610943ba8 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -905,6 +905,7 @@ package android.content.pm { method public boolean isFull(); method public boolean isGuest(); method public boolean isInitialized(); + method public boolean isMain(); method public boolean isManagedProfile(); method public boolean isPrimary(); method public boolean isProfile(); @@ -923,6 +924,7 @@ package android.content.pm { field public static final int FLAG_FULL = 1024; // 0x400 field @Deprecated public static final int FLAG_GUEST = 4; // 0x4 field public static final int FLAG_INITIALIZED = 16; // 0x10 + field public static final int FLAG_MAIN = 16384; // 0x4000 field @Deprecated public static final int FLAG_MANAGED_PROFILE = 32; // 0x20 field public static final int FLAG_PRIMARY = 1; // 0x1 field public static final int FLAG_PROFILE = 4096; // 0x1000 diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 9baa6ba2fb496..2be0323a1e8b8 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -158,6 +158,18 @@ public class UserInfo implements Parcelable { */ public static final int FLAG_EPHEMERAL_ON_CREATE = 0x00002000; + /** + * Indicates that this user is the designated main user on the device. This user may have access + * to certain features which are limited to at most one user. + * + *
Currently, this will be the first user to go through setup on the device, but in future + * releases this status may be transferable or may even not be given to any users. + * + *
This is not necessarily the system user. For example, it will not be the system user on + * devices for which {@link UserManager#isHeadlessSystemUserMode()} returns true. + */ + public static final int FLAG_MAIN = 0x00004000; + /** * @hide */ @@ -175,7 +187,8 @@ public class UserInfo implements Parcelable { FLAG_FULL, FLAG_SYSTEM, FLAG_PROFILE, - FLAG_EPHEMERAL_ON_CREATE + FLAG_EPHEMERAL_ON_CREATE, + FLAG_MAIN }) @Retention(RetentionPolicy.SOURCE) public @interface UserInfoFlag { @@ -368,6 +381,13 @@ public class UserInfo implements Parcelable { return (flags & FLAG_FULL) == FLAG_FULL; } + /** + * @see #FLAG_MAIN + */ + public boolean isMain() { + return (flags & FLAG_MAIN) == FLAG_MAIN; + } + /** * Returns true if the user is a split system user. *
If {@link UserManager#isSplitSystemUser split system user mode} is not enabled, diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index fbfe548416fe6..1f21bfe6cd729 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2341,12 +2341,18 @@ public class UserManager { } /** - * Used to check if the context user is the primary user. The primary user - * is the first human user on a device. This is not supported in headless system user mode. + * Used to check if the context user is the primary user. The primary user is the first human + * user on a device. This is not supported in headless system user mode. * * @return whether the context user is the primary user. + * + * @deprecated This method always returns true for the system user, who may not be a full user + * if {@link #isHeadlessSystemUserMode} is true. Use {@link #isSystemUser}, {@link #isAdminUser} + * or {@link #isMainUser} instead. + * * @hide */ + @Deprecated @SystemApi @RequiresPermission(anyOf = { Manifest.permission.MANAGE_USERS, @@ -2370,6 +2376,29 @@ public class UserManager { return getContextUserIfAppropriate() == UserHandle.USER_SYSTEM; } + /** + * Returns true if the context user is the designated "main user" of the device. This user may + * have access to certain features which are limited to at most one user. + * + *
Currently, the first human user on the device will be the main user; in the future, the + * concept may be transferable, so a different user (or even no user at all) may be designated + * the main user instead. + * + *
Note that this will be the not be the system user on devices for which
+ * {@link #isHeadlessSystemUserMode()} returns true.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
+ @UserHandleAware
+ public boolean isMainUser() {
+ final UserInfo user = getUserInfo(mUserId);
+ return user != null && user.isMain();
+ }
+
/**
* Used to check if the context user is an admin user. An admin user is allowed to
* modify or configure certain settings that aren't available to non-admin users,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index ac487fa0b1b7e..ce4a2ed488baa 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -265,7 +265,7 @@ public class UserManagerService extends IUserManager.Stub {
@VisibleForTesting
static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100;
- private static final int USER_VERSION = 10;
+ private static final int USER_VERSION = 11;
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
@@ -3355,6 +3355,7 @@ public class UserManagerService extends IUserManager.Stub {
final int oldFlags = systemUserData.info.flags;
final int newFlags;
final String newUserType;
+ // TODO(b/256624031): Also handle FLAG_MAIN
if (newHeadlessSystemUserMode) {
newUserType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
newFlags = oldFlags & ~UserInfo.FLAG_FULL;
@@ -3647,6 +3648,22 @@ public class UserManagerService extends IUserManager.Stub {
userVersion = 10;
}
+ if (userVersion < 11) {
+ // Add FLAG_MAIN
+ if (isHeadlessSystemUserMode()) {
+ final UserInfo earliestCreatedUser = getEarliestCreatedFullUser();
+ earliestCreatedUser.flags |= UserInfo.FLAG_MAIN;
+ userIdsToWrite.add(earliestCreatedUser.id);
+ } else {
+ synchronized (mUsersLock) {
+ final UserData userData = mUsers.get(UserHandle.USER_SYSTEM);
+ userData.info.flags |= UserInfo.FLAG_MAIN;
+ userIdsToWrite.add(userData.info.id);
+ }
+ }
+ userVersion = 11;
+ }
+
// Reminder: If you add another upgrade, make sure to increment USER_VERSION too.
// Done with userVersion changes, moving on to deal with userTypeVersion upgrades
@@ -3776,6 +3793,21 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.profileBadge = getFreeProfileBadgeLU(userInfo.profileGroupId, userInfo.userType);
}
+ private UserInfo getEarliestCreatedFullUser() {
+ final List