From 118b1bce2e14c7a543865d5af386f38c43c54781 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 17 Feb 2023 15:04:46 +0000 Subject: [PATCH] Make isAdminUser a public API Made isAdminUser a public API. Introduced new isAdminUser method in UMS to be used by the UM.isAdminUser public API without any permissions if called from the same profile group. Test: atest UserManagerTest -c Bug: 268350483 abandoned-master-cl: https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/base/+/21342103 Change-Id: I8572fe22a3c687ed3879e6ed77ab862ace86bc72 --- core/api/current.txt | 1 + core/api/system-current.txt | 1 - core/java/android/os/IUserManager.aidl | 1 + core/java/android/os/UserManager.java | 24 +++++++++------ .../android/server/pm/UserManagerService.java | 30 +++++++++++++++++++ 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 651669c0e1004..8e8f6c3219200 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33793,6 +33793,7 @@ package android.os { 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 f544e40d4ff36..a4a161810ca2e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10934,7 +10934,6 @@ package android.os { method @NonNull @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public java.util.Set 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 3cf3ea234d30e..fcebb455d09df 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -117,6 +117,7 @@ interface IUserManager { 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 bbf7f810b3c00..24e21dc33a442 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2432,21 +2432,24 @@ public class UserManager { } /** - * 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(); + } } /** @@ -3970,6 +3973,9 @@ public class UserManager { * 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 58ae95568e3bc..98cef2291aa77 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2048,6 +2048,27 @@ public class UserManagerService extends IUserManager.Stub { + "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(); @@ -2061,6 +2082,15 @@ public class UserManagerService extends IUserManager.Stub { } } + @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");