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) {