Change some UserManager APIs to @SystemApi
Added @SystemApi to all APIs currently used by TvProfileService that were @hide only.
Bug: 139914710
Test: Apps built with stable_system or experimental_system can use the APIs
Change-Id: I3499244b4d545a7a9401c7941663045bcd89c6fe
diff --git a/api/current.txt b/api/current.txt
index 645eae1..8753d3b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36358,10 +36358,10 @@
method public static android.content.Intent createUserCreationIntent(@Nullable String, @Nullable String, @Nullable String, @Nullable android.os.PersistableBundle);
method @WorkerThread public android.os.Bundle getApplicationRestrictions(String);
method public long getSerialNumberForUser(android.os.UserHandle);
- method public int getUserCount();
+ method @RequiresPermission("android.permission.MANAGE_USERS") public int getUserCount();
method public long getUserCreationTime(android.os.UserHandle);
method public android.os.UserHandle getUserForSerialNumber(long);
- method public String getUserName();
+ method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional=true) public String getUserName();
method public java.util.List<android.os.UserHandle> getUserProfiles();
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);
@@ -36371,14 +36371,14 @@
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
method public boolean isUserAGoat();
- method public boolean isUserRunning(android.os.UserHandle);
- method public boolean isUserRunningOrStopping(android.os.UserHandle);
+ method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunning(android.os.UserHandle);
+ method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean isUserUnlocked();
- method public boolean isUserUnlocked(android.os.UserHandle);
- method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserUnlocked(android.os.UserHandle);
+ method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.MODIFY_QUIET_MODE"}, conditional=true) public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int);
method @Deprecated public boolean setRestrictionsChallenge(String);
- method @Deprecated public void setUserRestriction(String, boolean);
+ method @Deprecated @RequiresPermission("android.permission.MANAGE_USERS") public void setUserRestriction(String, boolean);
method @Deprecated public void setUserRestrictions(android.os.Bundle);
method @Deprecated public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
method public static boolean supportsMultipleUsers();
diff --git a/api/system-current.txt b/api/system-current.txt
index 69319193..ababc7f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -67,6 +67,7 @@
field public static final String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
field public static final String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
field public static final String CONTROL_VPN = "android.permission.CONTROL_VPN";
+ field public static final String CREATE_USERS = "android.permission.CREATE_USERS";
field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
@@ -326,7 +327,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.CREATE_USERS"}) public boolean isProfileForeground(@NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isProfileForeground(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
@@ -7614,6 +7615,7 @@
public class UserManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @Nullable String[]) throws android.os.UserManager.UserOperationException;
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
@@ -7621,22 +7623,26 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getUserProfiles(boolean);
method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability();
- method public boolean hasRestrictedProfiles();
+ 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(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isGuestUser();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isPrimaryUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean removeUser(@NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull android.os.UserHandle, @NonNull String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
@@ -7649,6 +7655,10 @@
field public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 4; // 0x4
field public static final int SWITCHABILITY_STATUS_USER_IN_CALL = 1; // 0x1
field public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 2; // 0x2
+ field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
+ field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
+ field public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
+ field public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
}
public static final class UserManager.EnforcingUser implements android.os.Parcelable {
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 33d6131..b10abe7 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -41,11 +41,11 @@
* END OF DO NOT MOVE
*/
- UserInfo createUser(in String name, in String userType, int flags);
- UserInfo preCreateUser(in String userType);
- UserInfo createProfileForUser(in String name, in String userType, int flags, int userId,
+ UserInfo createUserWithThrow(in String name, in String userType, int flags);
+ UserInfo preCreateUserWithThrow(in String userType);
+ UserInfo createProfileForUserWithThrow(in String name, in String userType, int flags, int userId,
in String[] disallowedPackages);
- UserInfo createRestrictedProfile(String name, int parentUserHandle);
+ UserInfo createRestrictedProfileWithThrow(String name, int parentUserHandle);
void setUserEnabled(int userId);
void setUserAdmin(int userId);
void evictCredentialEncryptionKey(int userId);
@@ -100,7 +100,7 @@
boolean isManagedProfile(int userId);
boolean isDemoUser(int userId);
boolean isPreCreated(int userId);
- UserInfo createProfileForUserEvenWhenDisallowed(in String name, in String userType, int flags,
+ UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,
int userId, in String[] disallowedPackages);
boolean isUserUnlockingOrUnlocked(int userId);
int getUserIconBadgeResId(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 12e843c..08e4c3a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -28,6 +28,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -50,6 +51,7 @@
import android.location.LocationManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.util.AndroidException;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
@@ -77,8 +79,12 @@
private static final String TAG = "UserManager";
@UnsupportedAppUsage
private final IUserManager mService;
+ /** Holding the Application context (not constructor param context). */
private final Context mContext;
+ /** 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;
@@ -87,6 +93,7 @@
* This type of user cannot be created; it can only pre-exist on first boot.
* @hide
*/
+ @SystemApi
public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
/**
@@ -95,6 +102,7 @@
* This is sometimes called an ordinary 'secondary user'.
* @hide
*/
+ @SystemApi
public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
/**
@@ -122,6 +130,7 @@
* The intended purpose is for work profiles, which are managed by a corporate entity.
* @hide
*/
+ @SystemApi
public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
/**
@@ -130,6 +139,7 @@
* This type of user cannot be created; it can only pre-exist on first boot.
* @hide
*/
+ @SystemApi
public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
/**
@@ -1435,6 +1445,62 @@
public @UserOperationResult int getUserOperationResult() {
return mUserOperationResult;
}
+
+ /**
+ * Returns a UserOperationException containing the same message and error code.
+ * @hide
+ */
+ public static UserOperationException from(ServiceSpecificException exception) {
+ return new UserOperationException(exception.getMessage(), exception.errorCode);
+ }
+ }
+
+ /**
+ * Converts the ServiceSpecificException into a UserOperationException or throws null;
+ *
+ * @param exception exception to convert.
+ * @param throwInsteadOfNull if an exception should be thrown or null returned.
+ * @return null if chosen not to throw exception.
+ * @throws UserOperationException
+ */
+ private <T> T returnNullOrThrowUserOperationException(ServiceSpecificException exception,
+ boolean throwInsteadOfNull) throws UserOperationException {
+ if (throwInsteadOfNull) {
+ throw UserOperationException.from(exception);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Thrown to indicate user operation failed. (Checked exception)
+ * @hide
+ */
+ public static class CheckedUserOperationException extends AndroidException {
+ private final @UserOperationResult int mUserOperationResult;
+
+ /**
+ * Constructs a CheckedUserOperationException with specific result code.
+ *
+ * @param message the detail message
+ * @param userOperationResult the result code
+ * @hide
+ */
+ public CheckedUserOperationException(String message,
+ @UserOperationResult int userOperationResult) {
+ super(message);
+ mUserOperationResult = userOperationResult;
+ }
+
+ /** Returns the operation result code. */
+ public @UserOperationResult int getUserOperationResult() {
+ return mUserOperationResult;
+ }
+
+ /** Return a ServiceSpecificException containing the same message and error code. */
+ public ServiceSpecificException toServiceSpecificException() {
+ return new ServiceSpecificException(mUserOperationResult, getMessage());
+ }
}
/** @hide */
@@ -1447,6 +1513,7 @@
public UserManager(Context context, IUserManager service) {
mService = service;
mContext = context.getApplicationContext();
+ mUserId = context.getUserId();
}
/**
@@ -1586,17 +1653,25 @@
}
/**
- * Returns the user name of the user making this call. This call is only
- * available to applications on the system image; it requires the
- * {@code android.permission.MANAGE_USERS} or {@code android.permission.GET_ACCOUNTS_PRIVILEGED}
- * permissions.
+ * Returns the user name of the context user. This call is only available to applications on
+ * the system image; it requires the {@code android.permission.MANAGE_USERS} or {@code
+ * android.permission.GET_ACCOUNTS_PRIVILEGED} permissions.
+ *
* @return the user name
*/
- public String getUserName() {
- try {
- return mService.getUserName();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional = true)
+ @UserHandleAware
+ public @NonNull String getUserName() {
+ if (UserHandle.myUserId() == mUserId) {
+ try {
+ return mService.getUserName();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ } else {
+ UserInfo userInfo = getUserInfo(mUserId);
+ return userInfo == null ? "" : userInfo.name;
}
}
@@ -1639,7 +1714,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean isPrimaryUser() {
UserInfo user = getUserInfo(UserHandle.myUserId());
return user != null && user.isPrimary();
@@ -1676,22 +1752,26 @@
* user.
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean isUserAdmin(@UserIdInt int userId) {
UserInfo user = getUserInfo(userId);
return user != null && user.isAdmin();
}
/**
- * Returns whether the current user is of the given user type, such as
+ * Returns whether the context user's user is of the given user type, such as
* {@link UserManager#USER_TYPE_FULL_GUEST}.
*
* @return true if the user is of the given user type.
* @hide
*/
+ @SystemApi
+ @UserHandleAware
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean isUserOfType(@NonNull String userType) {
try {
- return mService.isUserOfType(UserHandle.myUserId(), userType);
+ return mService.isUserOfType(mUserId, userType);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -1707,6 +1787,7 @@
* @return true if the userHandle user is of type userType
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean isUserOfType(@NonNull UserHandle userHandle, @NonNull String userType) {
try {
@@ -1786,7 +1867,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean isRestrictedProfile(@NonNull UserHandle user) {
try {
return mService.getUserInfo(user.getIdentifier()).isRestricted();
@@ -1799,6 +1881,7 @@
* Checks if specified user can have restricted profile.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean canHaveRestrictedProfile(@UserIdInt int userId) {
try {
return mService.canHaveRestrictedProfile(userId);
@@ -1813,6 +1896,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean hasRestrictedProfiles() {
try {
return mService.hasRestrictedProfiles();
@@ -1827,6 +1911,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean isGuestUser(@UserIdInt int userId) {
UserInfo user = getUserInfo(userId);
return user != null && user.isGuest();
@@ -1839,7 +1925,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean isGuestUser() {
UserInfo user = getUserInfo(UserHandle.myUserId());
return user != null && user.isGuest();
@@ -1861,48 +1948,46 @@
}
/**
- * Checks if the calling app is running in a profile.
+ * Checks if the calling context user is running in a profile.
+ *
+ * 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 specified user.
*
* @return whether the caller is in a profile.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ @UserHandleAware
public boolean isProfile() {
- // 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;
- }
- try {
- mIsProfileCached = mService.isProfile(UserHandle.myUserId());
- return mIsProfileCached;
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ return isProfile(mUserId);
+ }
+
+ private boolean isProfile(@UserIdInt int userId) {
+ 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;
+ }
+ try {
+ mIsProfileCached = mService.isProfile(userId);
+ return mIsProfileCached;
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ } else {
+ try {
+ return mService.isProfile(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
}
/**
- * Checks if the specified user is a profile.
- *
- * 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 specified user.
- *
- * @return whether the specified user is a profile.
- * @hide
- */
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
- public boolean isProfile(@UserIdInt int userId) {
- if (userId == UserHandle.myUserId()) {
- return isProfile();
- }
- try {
- return mService.isProfile(userId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
- /**
* Checks if the calling app is running in a managed profile.
*
* @return whether the caller is in a managed profile.
@@ -1931,7 +2016,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isManagedProfile(@UserIdInt int userId) {
if (userId == UserHandle.myUserId()) {
return isManagedProfile();
@@ -1949,6 +2035,8 @@
* @return whether the caller is an ephemeral user.
* @hide
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean isEphemeralUser() {
return isUserEphemeral(UserHandle.myUserId());
}
@@ -1957,6 +2045,8 @@
* Returns whether the specified user is ephemeral.
* @hide
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean isUserEphemeral(@UserIdInt int userId) {
final UserInfo user = getUserInfo(userId);
return user != null && user.isEphemeral();
@@ -1978,12 +2068,15 @@
*
* @param user The user to retrieve the running state for.
*/
- // Note this requires either INTERACT_ACROSS_USERS or MANAGE_USERS.
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserRunning(UserHandle user) {
return isUserRunning(user.getIdentifier());
}
/** {@hide} */
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserRunning(@UserIdInt int userId) {
try {
return mService.isUserRunning(userId);
@@ -2007,7 +2100,8 @@
*
* @param user The user to retrieve the running state for.
*/
- // Note this requires either INTERACT_ACROSS_USERS or MANAGE_USERS.
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserRunningOrStopping(UserHandle user) {
try {
// TODO: reconcile stopped vs stopping?
@@ -2054,12 +2148,16 @@
* @see Intent#ACTION_USER_UNLOCKED
* @see Context#createDeviceProtectedStorageContext()
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserUnlocked(UserHandle user) {
return isUserUnlocked(user.getIdentifier());
}
/** {@hide} */
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserUnlocked(@UserIdInt int userId) {
try {
return mService.isUserUnlocked(userId);
@@ -2087,15 +2185,15 @@
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {
- Manifest.permission.MANAGE_USERS,
- Manifest.permission.INTERACT_ACROSS_USERS
- })
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserUnlockingOrUnlocked(@NonNull UserHandle user) {
return isUserUnlockingOrUnlocked(user.getIdentifier());
}
/** {@hide} */
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
try {
return mService.isUserUnlockingOrUnlocked(userId);
@@ -2136,12 +2234,13 @@
/**
* Returns the UserInfo object describing a specific user.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @param userId the user handle of the user whose information is being requested.
* @return the UserInfo object for a specific user.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public UserInfo getUserInfo(@UserIdInt int userId) {
try {
return mService.getUserInfo(userId);
@@ -2231,6 +2330,7 @@
* @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
*/
@UnsupportedAppUsage
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
public boolean hasBaseUserRestriction(@UserRestrictionKey String restrictionKey,
UserHandle userHandle) {
try {
@@ -2271,6 +2371,7 @@
* android.content.ComponentName, String)} instead.
*/
@Deprecated
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
public void setUserRestriction(String key, boolean value) {
setUserRestriction(key, value, Process.myUserHandle());
}
@@ -2278,7 +2379,6 @@
/**
* @hide
* Sets the value of a specific restriction on a specific user.
- * Requires the MANAGE_USERS permission.
* @param key the key of the restriction
* @param value the value for the restriction
* @param userHandle the user whose restriction is to be changed.
@@ -2288,6 +2388,7 @@
* android.content.ComponentName, String)} instead.
*/
@Deprecated
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
public void setUserRestriction(String key, boolean value, UserHandle userHandle) {
try {
mService.setUserRestriction(key, value, userHandle.getIdentifier());
@@ -2440,21 +2541,32 @@
* Creates a user with the specified name and options. For non-admin users, default user
* restrictions will be applied.
*
- * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
+ * {@link android.Manifest.permission#CREATE_USERS} suffices if flags are in
+ * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION}.
*
- * @param name the user's name
+ * @param name the user's name
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
- * @param flags UserInfo flags that specify user properties.
- * @see UserInfo
+ * @param flags UserInfo flags that specify user properties.
+ * @return the {@link UserInfo} object for the created user,
+ * or throws {@link UserOperationException} if the user could not be created
+ * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
+ * (otherwise returns {@code null}).
*
- * @return the UserInfo object for the created user, or {@code null} if the user could not be
- * created.
+ * @throws UserOperationException if the user could not be created and the calling app is
+ * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
+ * @see UserInfo
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
@UserInfoFlag int flags) {
try {
- return mService.createUser(name, userType, flags);
+ return mService.createUserWithThrow(name, userType, flags);
+ } catch (ServiceSpecificException e) {
+ return returnNullOrThrowUserOperationException(e,
+ mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2476,21 +2588,30 @@
*
* <p>All pre-created users are removed during system upgrade.
*
- * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
+ * {@link android.Manifest.permission#CREATE_USERS} suffices if flags are in
+ * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION}.
*
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
- * @return the UserInfo object for the created user, or {@code null} if the user could not be
- * created.
+ * @return the {@link UserInfo} object for the created user,
+ * or throws {@link UserOperationException} if the user could not be created
+ * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
+ * (otherwise returns {@code null}).
*
- * @throw {@link IllegalArgumentException} if {@code flags} contains
- * {@link UserInfo#FLAG_MANAGED_PROFILE}.
+ * @throws UserOperationException if the user could not be created and the calling app is
+ * targeting {@link android.os.Build.VERSION_CODES#R} or above.
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
- public @Nullable UserInfo preCreateUser(@NonNull String userType) {
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ public @Nullable UserInfo preCreateUser(@NonNull String userType)
+ throws UserOperationException {
try {
- return mService.preCreateUser(userType);
+ return mService.preCreateUserWithThrow(userType);
+ } catch (ServiceSpecificException e) {
+ return returnNullOrThrowUserOperationException(e,
+ mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2500,16 +2621,28 @@
* Creates a guest user and configures it.
* @param context an application context
* @param name the name to set for the user
+ * @return the {@link UserInfo} object for the created user,
+ * or throws {@link UserOperationException} if the user could not be created
+ * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
+ * (otherwise returns {@code null}).
+ *
+ * @throws UserOperationException if the user could not be created and the calling app is
+ * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
- public UserInfo createGuest(Context context, String name) {
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ public UserInfo createGuest(Context context, String name) throws UserOperationException {
UserInfo guest = null;
try {
- guest = mService.createUser(name, USER_TYPE_FULL_GUEST, 0);
+ guest = mService.createUserWithThrow(name, USER_TYPE_FULL_GUEST, 0);
if (guest != null) {
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
}
+ } catch (ServiceSpecificException e) {
+ return returnNullOrThrowUserOperationException(e,
+ context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2531,9 +2664,42 @@
}
/**
+ * Creates a user with the specified name and options as a profile of the context's user.
+ *
+ * @param name the user's name.
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @param disallowedPackages packages to not install for this profile.
+ *
+ * @return the {@link android.os.UserHandle} object for the created user,
+ * or throws {@link UserOperationException} if the user could not be created
+ * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
+ * (otherwise returns {@code null}).
+ *
+ * @throws UserOperationException if the user could not be created and the calling app is
+ * targeting {@link android.os.Build.VERSION_CODES#R} or above.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ @UserHandleAware
+ public @Nullable UserHandle createProfile(@NonNull String name, @NonNull String userType,
+ @Nullable String[] disallowedPackages) throws UserOperationException {
+ try {
+ return mService.createProfileForUserWithThrow(name, userType, 0,
+ mUserId, disallowedPackages).getUserHandle();
+ } catch (ServiceSpecificException e) {
+ return returnNullOrThrowUserOperationException(e,
+ mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a user with the specified name and options as a profile of another user.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
- * The type of profile must be specified using the given flags.
+ * <p>Requires MANAGE_USERS. CREATE_USERS suffices for ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION
*
* @param name the user's name
* @param flags flags that identify the type of user and other properties.
@@ -2546,6 +2712,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
@Deprecated
public UserInfo createProfileForUser(String name, @UserInfoFlag int flags,
@UserIdInt int userId) {
@@ -2555,7 +2723,6 @@
/**
* Creates a user with the specified name and options as a profile of another user.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @param name the user's name
* @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
@@ -2566,6 +2733,8 @@
* could not be created.
* @hide
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public UserInfo createProfileForUser(String name, @NonNull String userType,
@UserInfoFlag int flags, @UserIdInt int userId) {
return createProfileForUser(name, userType, flags, userId, null);
@@ -2582,14 +2751,26 @@
* @param userId new user will be a profile of this user.
* @param disallowedPackages packages that will not be installed in the profile being created.
*
- * @return the {@link UserInfo} object for the created user, or null if the user
- * could not be created.
+ * @return the {@link UserInfo} object for the created user,
+ * or throws {@link UserOperationException} if the user could not be created
+ * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
+ * (otherwise returns {@code null}).
+ *
+ * @throws UserOperationException if the user could not be created and the calling app is
+ * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public UserInfo createProfileForUser(String name, @NonNull String userType,
- @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
+ @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages)
+ throws UserOperationException {
try {
- return mService.createProfileForUser(name, userType, flags, userId, disallowedPackages);
+ return mService.createProfileForUserWithThrow(name, userType, flags, userId,
+ disallowedPackages);
+ } catch (ServiceSpecificException e) {
+ return returnNullOrThrowUserOperationException(e,
+ mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2598,17 +2779,21 @@
/**
* Similar to {@link #createProfileForUser(String, String, int, int, String[])}
* except bypassing the checking of {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @see #createProfileForUser(String, String, int, int, String[])
* @hide
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public UserInfo createProfileForUserEvenWhenDisallowed(String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
- String[] disallowedPackages) {
+ String[] disallowedPackages) throws UserOperationException {
try {
- return mService.createProfileForUserEvenWhenDisallowed(name, userType, flags,
+ return mService.createProfileForUserEvenWhenDisallowedWithThrow(name, userType, flags,
userId, disallowedPackages);
+ } catch (ServiceSpecificException e) {
+ return returnNullOrThrowUserOperationException(e,
+ mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2619,19 +2804,30 @@
* restrictions and adds shared accounts.
*
* @param name profile's name
- * @return UserInfo object for the created user, or null if the user could not be created.
+ * @return the {@link UserInfo} object for the created user,
+ * or throws {@link UserOperationException} if the user could not be created
+ * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
+ * (otherwise returns {@code null}).
+ *
+ * @throws UserOperationException if the user could not be created and the calling app is
+ * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
- public UserInfo createRestrictedProfile(String name) {
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ public UserInfo createRestrictedProfile(String name) throws UserOperationException {
try {
UserHandle parentUserHandle = Process.myUserHandle();
- UserInfo user = mService.createRestrictedProfile(name,
+ UserInfo user = mService.createRestrictedProfileWithThrow(name,
parentUserHandle.getIdentifier());
if (user != null) {
AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
UserHandle.of(user.id));
}
return user;
+ } catch (ServiceSpecificException e) {
+ return returnNullOrThrowUserOperationException(e,
+ mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2748,6 +2944,7 @@
* @param accountOptions
* @see #createUserCreationIntent(String, String, String, PersistableBundle)
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public void setSeedAccountData(int userId, String accountName, String accountType,
PersistableBundle accountOptions) {
try {
@@ -2779,6 +2976,7 @@
* @param userId
* @return
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean markGuestForDeletion(@UserIdInt int userId) {
try {
return mService.markGuestForDeletion(userId);
@@ -2790,8 +2988,6 @@
/**
* Sets the user as enabled, if such an user exists.
*
- * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
- *
* <p>Note that the default is true, it's only that managed profiles might not be enabled.
* Also ephemeral users can be disabled to indicate that their removal is in progress and they
* shouldn't be re-entered. Therefore ephemeral users should not be re-enabled once disabled.
@@ -2799,6 +2995,7 @@
* @param userId the id of the profile to enable
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public void setUserEnabled(@UserIdInt int userId) {
try {
mService.setUserEnabled(userId);
@@ -2832,6 +3029,7 @@
*
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public void evictCredentialEncryptionKey(@UserIdInt int userId) {
try {
mService.evictCredentialEncryptionKey(userId);
@@ -2845,6 +3043,7 @@
* <p>This API is not for use by third-party apps. It requires the {@code MANAGE_USERS}
* permission.</p>
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public int getUserCount() {
List<UserInfo> users = getUsers();
return users != null ? users.size() : 1;
@@ -2963,11 +3162,11 @@
/**
* Returns information for Primary user.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @return the Primary user, null if not found.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public @Nullable UserInfo getPrimaryUser() {
try {
return mService.getPrimaryUser();
@@ -2983,6 +3182,7 @@
* @return true if more users can be added, false if limit has been reached.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean canAddMoreUsers() {
// TODO(b/142482943): UMS has different logic, excluding Demo and Profile from counting. Why
// not here? The logic is inconsistent. See UMS.canAddMoreManagedProfiles
@@ -3007,6 +3207,7 @@
* @return true if more managed profiles can be added, false if limit has been reached.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
try {
return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
@@ -3036,12 +3237,15 @@
* Note that this returns both enabled and not enabled profiles. See
* {@link #getEnabledProfiles(int)} if you need only the enabled ones.
*
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
+ * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user.
* @param userId profiles of this user will be returned.
* @return the list of profiles.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}, conditional = true)
public List<UserInfo> getProfiles(@UserIdInt int userId) {
try {
return mService.getProfiles(userId, false /* enabledOnly */);
@@ -3057,7 +3261,6 @@
* @param otherUser one of the two user handles to check.
* @return true if the two users are in the same profile group.
*
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @hide
*/
@SystemApi
@@ -3067,12 +3270,13 @@
}
/**
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * Checks if the 2 provided user ids belong to the same profile group.
* @param userId one of the two user ids to check.
* @param otherUserId one of the two user ids to check.
* @return true if the two user ids are in the same profile group.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) {
try {
return mService.isSameProfileGroup(userId, otherUserId);
@@ -3085,12 +3289,15 @@
* Returns list of the profiles of userId including userId itself.
* Note that this returns only enabled.
*
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
+ * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user.
* @param userId profiles of this user will be returned.
* @return the list of profiles.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}, conditional = true)
public List<UserInfo> getEnabledProfiles(@UserIdInt int userId) {
try {
return mService.getProfiles(userId, true /* enabledOnly */);
@@ -3115,6 +3322,28 @@
}
/**
+ * Returns a list of ids for profiles associated with the context user including the user
+ * itself.
+ *
+ * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
+ * @return A non-empty list of UserHandles associated with the calling user.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}, conditional = true)
+ @UserHandleAware
+ public @NonNull List<UserHandle> getUserProfiles(boolean enabledOnly) {
+ final int[] userIds = getProfileIds(mUserId, enabledOnly);
+ final List<UserHandle> result = new ArrayList<>(userIds.length);
+ for (int userId : userIds) {
+ result.add(UserHandle.of(userId));
+ }
+ return result;
+ }
+
+ /**
* Returns a list of ids for profiles associated with the specified user including the user
* itself.
*
@@ -3139,6 +3368,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}, conditional = true)
public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
return getProfileIds(userId, false /* enabledOnly */);
}
@@ -3147,6 +3378,8 @@
* @see #getProfileIds(int, boolean)
* @hide
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}, conditional = true)
public int[] getEnabledProfileIds(@UserIdInt int userId) {
return getProfileIds(userId, true /* enabledOnly */);
}
@@ -3158,6 +3391,7 @@
*
* @hide
*/
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
public int getCredentialOwnerProfile(@UserIdInt int userId) {
try {
return mService.getCredentialOwnerProfile(userId);
@@ -3173,6 +3407,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public UserInfo getProfileParent(@UserIdInt int userId) {
try {
return mService.getProfileParent(userId);
@@ -3227,6 +3462,8 @@
*
* @see #isQuietModeEnabled(UserHandle)
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ Manifest.permission.MODIFY_QUIET_MODE}, conditional = true)
public boolean requestQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle) {
return requestQuietModeEnabled(enableQuietMode, userHandle, null);
}
@@ -3258,6 +3495,7 @@
* @see {@link #requestQuietModeEnabled(boolean, UserHandle)}
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean requestQuietModeEnabled(
boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target) {
return requestQuietModeEnabled(enableQuietMode, userHandle, target, 0);
@@ -3319,15 +3557,16 @@
}
/**
- * Returns whether the calling app's user has a badge (generally to put on profiles' icons).
+ * Returns whether the user associated with the context has a badge (generally to put on
+ * profiles' icons).
*
* @return true if the user's icons should display a badge; false otherwise.
- *
* @see #getBadgedIconForUser more information about badging in general
* @hide
*/
+ @UserHandleAware
public boolean hasBadge() {
- return hasBadge(UserHandle.myUserId());
+ return hasBadge(mUserId);
}
/**
@@ -3485,11 +3724,12 @@
/**
* Removes a user and all associated data.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @param userId the integer handle of the user.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean removeUser(@UserIdInt int userId) {
try {
return mService.removeUser(userId);
@@ -3507,7 +3747,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean removeUser(@NonNull UserHandle user) {
if (user == null) {
throw new IllegalArgumentException("user cannot be null");
@@ -3524,6 +3765,8 @@
* @see {@link #removeUser(int)}
* @hide
*/
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
try {
return mService.removeUserEvenWhenDisallowed(userId);
@@ -3534,12 +3777,12 @@
/**
* Updates the user's name.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @param userId the user's integer id
* @param name the new name for the user
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public void setUserName(@UserIdInt int userId, String name) {
try {
mService.setUserName(userId, name);
@@ -3549,16 +3792,16 @@
}
/**
- * Updates the calling user's name.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * Updates the context user's name.
*
* @param name the new name for the user
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @UserHandleAware
public void setUserName(@Nullable String name) {
- setUserName(getUserHandle(), name);
+ setUserName(mUserId, name);
}
/**
@@ -3567,35 +3810,41 @@
* @param icon the bitmap to set as the photo.
* @hide
*/
- public void setUserIcon(@UserIdInt int userId, Bitmap icon) {
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public void setUserIcon(@UserIdInt int userId, @NonNull Bitmap icon)
+ throws UserOperationException {
try {
mService.setUserIcon(userId, icon);
+ } catch (ServiceSpecificException e) {
+ throw UserOperationException.from(e);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
- * Sets the calling user's photo.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * Sets the context user's photo.
*
* @param icon the bitmap to set as the photo.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
- public void setUserIcon(@NonNull Bitmap icon) {
- setUserIcon(getUserHandle(), icon);
+ @UserHandleAware
+ public void setUserIcon(@NonNull Bitmap icon) throws UserOperationException {
+ setUserIcon(mUserId, icon);
}
/**
- * Returns a file descriptor for the user's photo. PNG data can be read from this file.
+ * Returns a bitmap of the user's photo
* @param userId the user whose photo we want to read.
* @return a {@link Bitmap} of the user's photo, or null if there's no photo.
* @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
public Bitmap getUserIcon(@UserIdInt int userId) {
try {
ParcelFileDescriptor fd = mService.getUserIcon(userId);
@@ -3616,9 +3865,7 @@
}
/**
- * Returns a Bitmap for the calling user's photo.
- * Requires {@link android.Manifest.permission#MANAGE_USERS}
- * or {@link android.Manifest.permission#GET_ACCOUNTS_PRIVILEGED} permissions.
+ * Returns a Bitmap for the context user's photo.
*
* @return a {@link Bitmap} of the user's photo, or null if there's no photo.
* @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default.
@@ -3627,8 +3874,9 @@
@SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+ @UserHandleAware
public @Nullable Bitmap getUserIcon() {
- return getUserIcon(getUserHandle());
+ return getUserIcon(mUserId);
}
/**
@@ -3805,6 +4053,7 @@
* @hide
* Set restrictions that should apply to any future guest user that's created.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public void setDefaultGuestRestrictions(Bundle restrictions) {
try {
mService.setDefaultGuestRestrictions(restrictions);
@@ -3817,6 +4066,7 @@
* @hide
* Gets the default guest restrictions.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public Bundle getDefaultGuestRestrictions() {
try {
return mService.getDefaultGuestRestrictions();
@@ -3847,6 +4097,7 @@
* @param accountType The account type of the account to check for
* @return whether the seed account was found
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean someUserHasSeedAccount(String accountName, String accountType) {
try {
return mService.someUserHasSeedAccount(accountName, accountType);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7181142..24111e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2361,7 +2361,7 @@
<permission android:name="android.permission.MANAGE_USERS"
android:protectionLevel="signature|privileged" />
- <!-- @hide Allows an application to create, remove users and get the list of
+ <!-- @SystemApi @hide Allows an application to create, remove users and get the list of
users on the device. Applications holding this permission can only create restricted,
guest, managed, demo, and ephemeral users. For creating other kind of users,
{@link android.Manifest.permission#MANAGE_USERS} is needed.
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index d84197c..aedafbb 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -162,7 +162,8 @@
* createAndManageUser is called by the device owner.
*/
public abstract UserInfo createUserEvenWhenDisallowed(String name, String userType,
- int flags, String[] disallowedPackages);
+ int flags, String[] disallowedPackages)
+ throws UserManager.CheckedUserOperationException;
/**
* Same as {@link UserManager#removeUser(int userId)}, but bypasses the check for
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 5adab378..0cc6cd1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -81,6 +81,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -2519,7 +2520,7 @@
}
name = arg;
- UserInfo info;
+ UserInfo info = null;
IUserManager um = IUserManager.Stub.asInterface(
ServiceManager.getService(Context.USER_SERVICE));
IAccountManager accm = IAccountManager.Stub.asInterface(
@@ -2527,17 +2528,22 @@
if (userType == null) {
userType = UserInfo.getDefaultUserType(flags);
}
- if (UserManager.isUserTypeRestricted(userType)) {
- // In non-split user mode, userId can only be SYSTEM
- int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
- info = um.createRestrictedProfile(name, parentUserId);
- accm.addSharedAccountsFromParentUser(parentUserId, userId,
- (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
- } else if (userId < 0) {
- info = preCreateOnly ?
- um.preCreateUser(userType) : um.createUser(name, userType, flags);
- } else {
- info = um.createProfileForUser(name, userType, flags, userId, null);
+ try {
+ if (UserManager.isUserTypeRestricted(userType)) {
+ // In non-split user mode, userId can only be SYSTEM
+ int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
+ info = um.createRestrictedProfileWithThrow(name, parentUserId);
+ accm.addSharedAccountsFromParentUser(parentUserId, userId,
+ (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
+ } else if (userId < 0) {
+ info = preCreateOnly ?
+ um.preCreateUserWithThrow(userType) :
+ um.createUserWithThrow(name, userType, flags);
+ } else {
+ info = um.createProfileForUserWithThrow(name, userType, flags, userId, null);
+ }
+ } catch (ServiceSpecificException e) {
+ getErrPrintWriter().println("Error: " + e);
}
if (info != null) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2a249d2..5511a54 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -67,6 +67,7 @@
import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
@@ -1538,12 +1539,14 @@
@Override
public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
- checkManageUsersPermission("update users");
- if (hasUserRestriction(UserManager.DISALLOW_SET_USER_ICON, userId)) {
- Slog.w(LOG_TAG, "Cannot set user icon. DISALLOW_SET_USER_ICON is enabled.");
- return;
+ try {
+ checkManageUsersPermission("update users");
+ enforceUserRestriction(UserManager.DISALLOW_SET_USER_ICON, userId,
+ "Cannot set user icon");
+ mLocalService.setUserIcon(userId, bitmap);
+ } catch (UserManager.CheckedUserOperationException e) {
+ throw e.toServiceSpecificException();
}
- mLocalService.setUserIcon(userId, bitmap);
}
@@ -3037,34 +3040,53 @@
/**
* Creates a profile user. Used for actual profiles, like
- * {@link UserManager#USER_TYPE_PROFILE_MANAGED}, as well as for
- * {@link UserManager#USER_TYPE_FULL_RESTRICTED}.
+ * {@link UserManager#USER_TYPE_PROFILE_MANAGED},
+ * as well as for {@link UserManager#USER_TYPE_FULL_RESTRICTED}.
*/
@Override
- public UserInfo createProfileForUser(String name, @NonNull String userType,
- @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
+ public UserInfo createProfileForUserWithThrow(String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
+ throws ServiceSpecificException {
checkManageOrCreateUsersPermission(flags);
- return createUserInternal(name, userType, flags, userId, disallowedPackages);
+ try {
+ return createUserInternal(name, userType, flags, userId, disallowedPackages);
+ } catch (UserManager.CheckedUserOperationException e) {
+ throw e.toServiceSpecificException();
+ }
}
- /** @see #createProfileForUser */
+ /**
+ * @see #createProfileForUser
+ */
@Override
- public UserInfo createProfileForUserEvenWhenDisallowed(String name, @NonNull String userType,
- @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
+ public UserInfo createProfileForUserEvenWhenDisallowedWithThrow(String name,
+ @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
+ throws ServiceSpecificException {
checkManageOrCreateUsersPermission(flags);
- return createUserInternalUnchecked(name, userType, flags, userId,
- /* preCreate= */ false, disallowedPackages);
+ try {
+ return createUserInternalUnchecked(name, userType, flags, userId,
+ /* preCreate= */ false, disallowedPackages);
+ } catch (UserManager.CheckedUserOperationException e) {
+ throw e.toServiceSpecificException();
+ }
}
@Override
- public UserInfo createUser(String name, @NonNull String userType, @UserInfoFlag int flags) {
+ public UserInfo createUserWithThrow(String name, @NonNull String userType,
+ @UserInfoFlag int flags)
+ throws ServiceSpecificException {
checkManageOrCreateUsersPermission(flags);
- return createUserInternal(name, userType, flags, UserHandle.USER_NULL,
- /* disallowedPackages= */ null);
+ try {
+ return createUserInternal(name, userType, flags, UserHandle.USER_NULL,
+ /* disallowedPackages= */ null);
+ } catch (UserManager.CheckedUserOperationException e) {
+ throw e.toServiceSpecificException();
+ }
}
@Override
- public UserInfo preCreateUser(String userType) {
+ public UserInfo preCreateUserWithThrow(String userType) throws ServiceSpecificException {
final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
final int flags = userTypeDetails != null ? userTypeDetails.getDefaultUserInfoFlags() : 0;
@@ -3074,28 +3096,32 @@
"cannot pre-create user of type " + userType);
Slog.i(LOG_TAG, "Pre-creating user of type " + userType);
- return createUserInternalUnchecked(/* name= */ null, userType, flags,
- /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
- /* disallowedPackages= */ null);
+ try {
+ return createUserInternalUnchecked(/* name= */ null, userType, flags,
+ /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
+ /* disallowedPackages= */ null);
+ } catch (UserManager.CheckedUserOperationException e) {
+ throw e.toServiceSpecificException();
+ }
}
private UserInfo createUserInternal(@Nullable String name, @NonNull String userType,
@UserInfoFlag int flags, @UserIdInt int parentId,
- @Nullable String[] disallowedPackages) {
+ @Nullable String[] disallowedPackages)
+ throws UserManager.CheckedUserOperationException {
String restriction = (UserManager.isUserTypeManagedProfile(userType))
? UserManager.DISALLOW_ADD_MANAGED_PROFILE
: UserManager.DISALLOW_ADD_USER;
- if (hasUserRestriction(restriction, UserHandle.getCallingUserId())) {
- Slog.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
- return null;
- }
+ enforceUserRestriction(restriction, UserHandle.getCallingUserId(),
+ "Cannot add user");
return createUserInternalUnchecked(name, userType, flags, parentId,
/* preCreate= */ false, disallowedPackages);
}
private UserInfo createUserInternalUnchecked(@Nullable String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
- boolean preCreate, @Nullable String[] disallowedPackages) {
+ boolean preCreate, @Nullable String[] disallowedPackages)
+ throws UserManager.CheckedUserOperationException {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("createUser-" + flags);
try {
@@ -3109,7 +3135,7 @@
private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
boolean preCreate, @Nullable String[] disallowedPackages,
- @NonNull TimingsTraceAndSlog t) {
+ @NonNull TimingsTraceAndSlog t) throws UserManager.CheckedUserOperationException {
final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
if (userTypeDetails == null) {
Slog.e(LOG_TAG, "Cannot create user of invalid user type: " + userType);
@@ -3144,8 +3170,8 @@
DeviceStorageMonitorInternal dsm = LocalServices
.getService(DeviceStorageMonitorInternal.class);
if (dsm.isMemoryLow()) {
- Slog.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
- return null;
+ throwCheckedUserOperationException("Cannot add user. Not enough space on disk.",
+ UserManager.USER_OPERATION_ERROR_LOW_STORAGE);
}
final boolean isProfile = userTypeDetails.isProfile();
@@ -3164,41 +3190,50 @@
synchronized (mUsersLock) {
parent = getUserDataLU(parentId);
}
- if (parent == null) return null;
+ if (parent == null) {
+ throwCheckedUserOperationException(
+ "Cannot find user data for parent user " + parentId,
+ UserManager.USER_OPERATION_ERROR_UNKNOWN);
+ }
}
if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
- Slog.e(LOG_TAG, "Cannot add more users of type " + userType
- + ". Maximum number of that type already exists.");
- return null;
+ throwCheckedUserOperationException("Cannot add more users of type " + userType
+ + ". Maximum number of that type already exists.",
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
}
// TODO(b/142482943): Perhaps let the following code apply to restricted users too.
if (isProfile && !canAddMoreProfilesToUser(userType, parentId, false)) {
- Slog.e(LOG_TAG, "Cannot add more profiles of type " + userType
- + " for user " + parentId);
- return null;
+ throwCheckedUserOperationException(
+ "Cannot add more profiles of type " + userType
+ + " for user " + parentId,
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
}
if (!isGuest && !isProfile && !isDemo && isUserLimitReached()) {
// If we're not adding a guest/demo user or a profile and the 'user limit' has
// been reached, cannot add a user.
- Slog.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
- return null;
+ throwCheckedUserOperationException(
+ "Cannot add user. Maximum user limit is reached.",
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
}
// In legacy mode, restricted profile's parent can only be the owner user
if (isRestricted && !UserManager.isSplitSystemUser()
&& (parentId != UserHandle.USER_SYSTEM)) {
- Slog.w(LOG_TAG, "Cannot add restricted profile - parent user must be owner");
- return null;
+ throwCheckedUserOperationException(
+ "Cannot add restricted profile - parent user must be owner",
+ UserManager.USER_OPERATION_ERROR_UNKNOWN);
}
if (isRestricted && UserManager.isSplitSystemUser()) {
if (parent == null) {
- Slog.w(LOG_TAG, "Cannot add restricted profile - parent user must be "
- + "specified");
- return null;
+ throwCheckedUserOperationException(
+ "Cannot add restricted profile - parent user must be specified",
+ UserManager.USER_OPERATION_ERROR_UNKNOWN);
}
if (!parent.info.canHaveProfile()) {
- Slog.w(LOG_TAG, "Cannot add restricted profile - profiles cannot be "
- + "created for the specified parent user id " + parentId);
- return null;
+ throwCheckedUserOperationException(
+ "Cannot add restricted profile - profiles cannot be created for "
+ + "the specified parent user id "
+ + parentId,
+ UserManager.USER_OPERATION_ERROR_UNKNOWN);
}
}
@@ -3464,9 +3499,9 @@
* @hide
*/
@Override
- public UserInfo createRestrictedProfile(String name, int parentUserId) {
+ public UserInfo createRestrictedProfileWithThrow(String name, int parentUserId) {
checkManageOrCreateUsersPermission("setupRestrictedProfile");
- final UserInfo user = createProfileForUser(
+ final UserInfo user = createProfileForUserWithThrow(
name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
if (user == null) {
return null;
@@ -4715,7 +4750,8 @@
@Override
public UserInfo createUserEvenWhenDisallowed(String name, @NonNull String userType,
- @UserInfoFlag int flags, String[] disallowedPackages) {
+ @UserInfoFlag int flags, String[] disallowedPackages)
+ throws UserManager.CheckedUserOperationException {
return createUserInternalUnchecked(name, userType, flags,
UserHandle.USER_NULL, /* preCreated= */ false, disallowedPackages);
}
@@ -4875,6 +4911,38 @@
}
}
+ /**
+ * Check if user has restrictions
+ * @param restriction restrictions to check
+ * @param userId id of the user
+ *
+ * @throws {@link android.os.UserManager.CheckedUserOperationException} if user has any of the
+ * specified restrictions
+ */
+ private void enforceUserRestriction(String restriction, @UserIdInt int userId, String message)
+ throws UserManager.CheckedUserOperationException {
+ if (hasUserRestriction(restriction, userId)) {
+ String errorMessage = (message != null ? (message + ": ") : "")
+ + restriction + " is enabled.";
+ Slog.w(LOG_TAG, errorMessage);
+ throw new UserManager.CheckedUserOperationException(errorMessage,
+ UserManager.USER_OPERATION_ERROR_UNKNOWN);
+ }
+ }
+
+ /**
+ * Throws CheckedUserOperationException and shows error log
+ * @param message message for exception and logging
+ * @param userOperationResult result/error code
+ * @throws UserManager.CheckedUserOperationException
+ */
+ private void throwCheckedUserOperationException(@NonNull String message,
+ @UserManager.UserOperationResult int userOperationResult)
+ throws UserManager.CheckedUserOperationException {
+ Slog.e(LOG_TAG, message);
+ throw new UserManager.CheckedUserOperationException(message, userOperationResult);
+ }
+
/* Remove all the users except of the system one. */
private void removeNonSystemUsers() {
ArrayList<UserInfo> usersToRemove = new ArrayList<>();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index af57c29..05b1106 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10151,10 +10151,14 @@
UserHandle.myUserId(), ACTION_PROVISION_MANAGED_USER).toArray(
new String[0]);
}
- UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
- userType, userInfoFlags, disallowedPackages);
- if (userInfo != null) {
- user = userInfo.getUserHandle();
+ try {
+ UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
+ userType, userInfoFlags, disallowedPackages);
+ if (userInfo != null) {
+ user = userInfo.getUserHandle();
+ }
+ } catch (UserManager.CheckedUserOperationException e) {
+ Log.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e);
}
} finally {
mInjector.binderRestoreCallingIdentity(id);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3a07a69..ab1159e2 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -61,6 +61,7 @@
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index e375aef..9eaf8b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -23,7 +23,9 @@
import android.content.pm.UserInfo;
import android.os.Looper;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.UserManagerInternal;
import androidx.test.InstrumentationRegistry;
@@ -31,11 +33,13 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.server.storage.DeviceStorageMonitorInternal;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.util.List;
@@ -243,6 +247,65 @@
true /* allow remove */));
}
+ @Test
+ public void testCreateProfileForUser_lowStorageException() {
+ DeviceStorageMonitorInternal dsmMock = Mockito.mock(DeviceStorageMonitorInternal.class);
+ Mockito.when(dsmMock.isMemoryLow()).thenReturn(true);
+ LocalServices.addService(DeviceStorageMonitorInternal.class, dsmMock);
+
+ try {
+ mUserManagerService.createProfileForUserWithThrow("user2", USER_TYPE_PROFILE_MANAGED, 0,
+ UserHandle.USER_SYSTEM, null);
+ } catch (ServiceSpecificException e) {
+ assertEquals(UserManager.USER_OPERATION_ERROR_LOW_STORAGE,
+ UserManager.UserOperationException.from(e).getUserOperationResult());
+ } finally {
+ LocalServices.removeServiceForTest(DeviceStorageMonitorInternal.class);
+ }
+ }
+
+ @Test
+ public void testCreateProfileForUser_unknownParentUser() {
+ DeviceStorageMonitorInternal dsmMock = Mockito.mock(DeviceStorageMonitorInternal.class);
+ Mockito.when(dsmMock.isMemoryLow()).thenReturn(false);
+ LocalServices.addService(DeviceStorageMonitorInternal.class, dsmMock);
+
+ try {
+ final int badParentUserId = 1234;
+ mUserManagerService.createProfileForUserWithThrow("profile", USER_TYPE_PROFILE_MANAGED,
+ 0, badParentUserId, null);
+ } catch (ServiceSpecificException e) {
+ assertEquals(UserManager.USER_OPERATION_ERROR_UNKNOWN,
+ UserManager.UserOperationException.from(e).getUserOperationResult());
+ } finally {
+ LocalServices.removeServiceForTest(DeviceStorageMonitorInternal.class);
+ }
+ }
+
+ @Test
+ public void testCreateManagedProfileForUser_maxManagedUsersException() {
+ DeviceStorageMonitorInternal dsmMock = Mockito.mock(DeviceStorageMonitorInternal.class);
+ Mockito.when(dsmMock.isMemoryLow()).thenReturn(false);
+ LocalServices.addService(DeviceStorageMonitorInternal.class, dsmMock);
+
+ UserManagerService userManagerServiceSpy = Mockito.spy(mUserManagerService);
+ Mockito.doReturn(false).when(userManagerServiceSpy).canAddMoreManagedProfiles(
+ Mockito.anyInt(), Mockito.anyBoolean());
+
+ Mockito.doReturn(false).when(userManagerServiceSpy).canAddMoreProfilesToUser(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyBoolean());
+
+ try {
+ userManagerServiceSpy.createProfileForUserWithThrow("profile",
+ USER_TYPE_PROFILE_MANAGED, 0, UserHandle.USER_SYSTEM, null);
+ } catch (ServiceSpecificException e) {
+ assertEquals(UserManager.USER_OPERATION_ERROR_MAX_USERS,
+ UserManager.UserOperationException.from(e).getUserOperationResult());
+ } finally {
+ LocalServices.removeServiceForTest(DeviceStorageMonitorInternal.class);
+ }
+ }
+
private void removeUsers() {
List<UserInfo> users = mUserManagerService.getUsers(/* excludeDying */ false);
for (UserInfo user: users) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 77376f0..2469cec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -39,6 +39,7 @@
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Slog;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -69,8 +70,10 @@
// Packages which are used during tests.
private static final String[] PACKAGES = new String[] {
- "com.android.egg"
+ "com.android.egg",
+ "com.google.android.webview"
};
+ private static final String TAG = UserManagerTest.class.getSimpleName();
private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
@@ -333,6 +336,9 @@
assertThat(userInfo).isNotNull();
final int userId = userInfo.id;
+ UserManager userManagerForUser = (UserManager) mContext.createPackageContextAsUser(
+ "android", 0, asHandle(userId)).getSystemService(Context.USER_SERVICE);
+
assertThat(mUserManager.hasBadge(userId)).isEqualTo(userTypeDetails.hasBadge());
assertThat(mUserManager.getUserIconBadgeResId(userId))
.isEqualTo(userTypeDetails.getIconBadge());
@@ -340,9 +346,11 @@
.isEqualTo(userTypeDetails.getBadgePlain());
assertThat(mUserManager.getUserBadgeNoBackgroundResId(userId))
.isEqualTo(userTypeDetails.getBadgeNoBackground());
- assertThat(mUserManager.isProfile(userId)).isEqualTo(userTypeDetails.isProfile());
assertThat(mUserManager.isUserOfType(asHandle(userId), userTypeDetails.getName()))
.isTrue();
+ assertThat(userManagerForUser.isProfile()).isEqualTo(userTypeDetails.isProfile());
+ assertThat(userManagerForUser.isUserOfType(asHandle(userId), userTypeDetails.getName()))
+ .isTrue();
final int badgeIndex = userInfo.profileBadge;
assertThat(mUserManager.getUserBadgeColor(userId)).isEqualTo(
@@ -351,7 +359,7 @@
Resources.getSystem().getString(userTypeDetails.getBadgeLabel(badgeIndex), "Test"));
}
- // Make sure only one managed profile can be created
+ // Make sure only max managed profiles can be created
@MediumTest
@Test
public void testAddManagedProfile() throws Exception {
@@ -384,6 +392,11 @@
UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
// Verify that the packagesToVerify are installed by default.
for (String pkg : PACKAGES) {
+ if (!mPackageManager.isPackageAvailable(pkg)) {
+ Slog.w(TAG, "Package is not available " + pkg);
+ continue;
+ }
+
assertWithMessage("Package should be installed in managed profile: %s", pkg)
.that(isPackageInstalledForUser(pkg, userInfo1.id)).isTrue();
}
@@ -393,6 +406,11 @@
UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId, PACKAGES);
// Verify that the packagesToVerify are not installed by default.
for (String pkg : PACKAGES) {
+ if (!mPackageManager.isPackageAvailable(pkg)) {
+ Slog.w(TAG, "Package is not available " + pkg);
+ continue;
+ }
+
assertWithMessage(
"Package should not be installed in managed profile when disallowed: %s", pkg)
.that(isPackageInstalledForUser(pkg, userInfo2.id)).isFalse();
@@ -410,12 +428,22 @@
UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId, PACKAGES);
// Verify that the packagesToVerify are not installed by default.
for (String pkg : PACKAGES) {
+ if (!mPackageManager.isPackageAvailable(pkg)) {
+ Slog.w(TAG, "Package is not available " + pkg);
+ continue;
+ }
+
assertWithMessage("Pkg should not be installed in managed profile when disallowed: %s",
pkg).that(isPackageInstalledForUser(pkg, userInfo.id)).isFalse();
}
// Verify that the disallowed packages during profile creation can be installed now.
for (String pkg : PACKAGES) {
+ if (!mPackageManager.isPackageAvailable(pkg)) {
+ Slog.w(TAG, "Package is not available " + pkg);
+ continue;
+ }
+
assertWithMessage("Package could not be installed: %s", pkg)
.that(mPackageManager.installExistingPackageAsUser(pkg, userInfo.id))
.isEqualTo(PackageManager.INSTALL_SUCCEEDED);
@@ -774,6 +802,78 @@
assertThat(found).isTrue();
}
+ @Test
+ public void testCreateProfile_withContextUserId() throws Exception {
+ final int primaryUserId = mUserManager.getPrimaryUser().id;
+
+ UserInfo userProfile = createProfileForUser("Managed 1",
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
+ assertThat(userProfile).isNotNull();
+
+ UserManager um = (UserManager) mContext.createPackageContextAsUser(
+ "android", 0, mUserManager.getPrimaryUser().getUserHandle())
+ .getSystemService(Context.USER_SERVICE);
+
+ List<UserHandle> profiles = um.getUserProfiles(false);
+ assertThat(profiles.size()).isEqualTo(2);
+ assertThat(profiles.get(0).equals(userProfile.getUserHandle())
+ || profiles.get(1).equals(userProfile.getUserHandle())).isTrue();
+ }
+
+ @Test
+ public void testSetUserName_withContextUserId() throws Exception {
+ final int primaryUserId = mUserManager.getPrimaryUser().id;
+
+ UserInfo userInfo1 = createProfileForUser("Managed 1",
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
+ assertThat(userInfo1).isNotNull();
+
+ UserManager um = (UserManager) mContext.createPackageContextAsUser(
+ "android", 0, userInfo1.getUserHandle())
+ .getSystemService(Context.USER_SERVICE);
+
+ final String newName = "Managed_user 1";
+ um.setUserName(newName);
+
+ UserInfo userInfo = mUserManager.getUserInfo(userInfo1.id);
+ assertThat(userInfo.name).isEqualTo(newName);
+
+ // get user name from getUserName using context.getUserId
+ assertThat(um.getUserName()).isEqualTo(newName);
+ }
+
+ @Test
+ public void testGetUserName_withContextUserId() throws Exception {
+ final String userName = "User 2";
+ UserInfo user2 = createUser(userName, 0);
+ assertThat(user2).isNotNull();
+
+ UserManager um = (UserManager) mContext.createPackageContextAsUser(
+ "android", 0, user2.getUserHandle())
+ .getSystemService(Context.USER_SERVICE);
+
+ assertThat(um.getUserName()).isEqualTo(userName);
+ }
+
+ @Test
+ public void testGetUserIcon_withContextUserId() throws Exception {
+ final int primaryUserId = mUserManager.getPrimaryUser().id;
+
+ UserInfo userInfo1 = createProfileForUser("Managed 1",
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
+ assertThat(userInfo1).isNotNull();
+
+ UserManager um = (UserManager) mContext.createPackageContextAsUser(
+ "android", 0, userInfo1.getUserHandle())
+ .getSystemService(Context.USER_SERVICE);
+
+ final String newName = "Managed_user 1";
+ um.setUserName(newName);
+
+ UserInfo userInfo = mUserManager.getUserInfo(userInfo1.id);
+ assertThat(userInfo.name).isEqualTo(newName);
+ }
+
private boolean isPackageInstalledForUser(String packageName, int userId) {
try {
return mPackageManager.getPackageInfoAsUser(packageName, 0, userId) != null;