Merge "Make isAdminUser a public API" into udc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 52cb492..5af8079 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -33834,6 +33834,7 @@
method public android.os.Bundle getUserRestrictions();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(String);
+ method public boolean isAdminUser();
method public boolean isDemoUser();
method public static boolean isHeadlessSystemUserMode();
method public boolean isManagedProfile();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fb9ba62..744399d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10962,7 +10962,6 @@
method @NonNull @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public java.util.Set<android.os.UserHandle> getVisibleUsers();
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 public boolean isCloneProfile();
method @Deprecated public boolean isCredentialSharableWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3cf3ea2..fcebb45 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -117,6 +117,7 @@
boolean someUserHasAccount(in String accountName, in String accountType);
String getProfileType(int userId);
boolean isDemoUser(int userId);
+ boolean isAdminUser(int userId);
boolean isPreCreated(int userId);
UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,
int userId, in String[] disallowedPackages);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8d8deaa..290f929 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2433,21 +2433,24 @@
}
/**
- * Used to check if the context user is an admin user. An admin user is allowed to
+ * Used to check if the context user is an admin user. An admin user may be allowed to
* modify or configure certain settings that aren't available to non-admin users,
* create and delete additional users, etc. There can be more than one admin users.
*
* @return whether the context user is an admin user.
- * @hide
*/
- @SystemApi
- @RequiresPermission(anyOf = {
- Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS,
- Manifest.permission.QUERY_USERS})
- @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public boolean isAdminUser() {
- return isUserAdmin(getContextUserIfAppropriate());
+ try {
+ return mService.isAdminUser(getContextUserIfAppropriate());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -3972,6 +3975,9 @@
* time, the preferred user name and account information are used by the setup process for that
* user.
*
+ * This API should only be called if the current user is an {@link #isAdminUser() admin} user,
+ * as otherwise the returned intent will not be able to create a user.
+ *
* @param userName Optional name to assign to the user.
* @param accountName Optional account name that will be used by the setup wizard to initialize
* the user.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2b08a2c..b21243a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2049,6 +2049,27 @@
+ "permission to: check " + name);
}
+ /**
+ * Enforces that the calling user is in the same profile group as {@code userId} or that only
+ * the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
+ * can make certain calls to the UserManager.
+ *
+ * @param userId the user's id
+ * @param name used as message if SecurityException is thrown
+ * @throws SecurityException if the caller lacks the required permissions.
+ */
+ private void checkQueryOrCreateUsersPermissionIfCallerInOtherProfileGroup(
+ @UserIdInt int userId, String name) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId)) {
+ return;
+ }
+ checkQueryOrCreateUsersPermission(name);
+ }
+
@Override
public boolean isDemoUser(@UserIdInt int userId) {
final int callingUserId = UserHandle.getCallingUserId();
@@ -2063,6 +2084,15 @@
}
@Override
+ public boolean isAdminUser(@UserIdInt int userId) {
+ checkQueryOrCreateUsersPermissionIfCallerInOtherProfileGroup(userId, "isAdminUser");
+ synchronized (mUsersLock) {
+ final UserInfo userInfo = getUserInfoLU(userId);
+ return userInfo != null && userInfo.isAdmin();
+ }
+ }
+
+ @Override
public boolean isPreCreated(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isPreCreated");
synchronized (mUsersLock) {