Loading core/api/system-current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -8681,7 +8681,11 @@ package android.os { } public final class NewUserRequest { method @Nullable public String getAccountName(); method @Nullable public android.os.PersistableBundle getAccountOptions(); method @Nullable public String getAccountType(); method @Nullable public String getName(); method @Nullable public android.graphics.Bitmap getUserIcon(); method @NonNull public String getUserType(); method public boolean isAdmin(); method public boolean isEphemeral(); Loading @@ -8690,9 +8694,13 @@ package android.os { public static final class NewUserRequest.Builder { ctor public NewUserRequest.Builder(); method @NonNull public android.os.NewUserRequest build(); method @NonNull public android.os.NewUserRequest.Builder setAccountName(@Nullable String); method @NonNull public android.os.NewUserRequest.Builder setAccountOptions(@Nullable android.os.PersistableBundle); method @NonNull public android.os.NewUserRequest.Builder setAccountType(@Nullable String); method @NonNull public android.os.NewUserRequest.Builder setAdmin(); method @NonNull public android.os.NewUserRequest.Builder setEphemeral(); method @NonNull public android.os.NewUserRequest.Builder setName(@Nullable String); method @NonNull public android.os.NewUserRequest.Builder setUserIcon(@Nullable android.graphics.Bitmap); method @NonNull public android.os.NewUserRequest.Builder setUserType(@NonNull String); } Loading Loading @@ -8970,6 +8978,7 @@ package android.os { 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); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull 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"; field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; Loading @@ -8981,6 +8990,7 @@ package android.os { 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 int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7; // 0x7 field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST"; 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"; core/java/android/os/IUserManager.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ package android.os; import android.os.Bundle; import android.os.IUserRestrictionsListener; import android.os.PersistableBundle; import android.os.UserHandle; import android.os.UserManager; import android.content.pm.UserInfo; import android.content.IntentSender; Loading Loading @@ -91,6 +92,9 @@ interface IUserManager { boolean markGuestForDeletion(int userId); UserInfo findCurrentGuestUser(); boolean isQuietModeEnabled(int userId); UserHandle createUserWithAttributes(in String userName, in String userType, int flags, in Bitmap userIcon, in String accountName, in String accountType, in PersistableBundle accountOptions); void setSeedAccountData(int userId, in String accountName, in String accountType, in PersistableBundle accountOptions, boolean persist); String getSeedAccountName(int userId); Loading @@ -98,6 +102,7 @@ interface IUserManager { PersistableBundle getSeedAccountOptions(int userId); void clearSeedAccountData(int userId); boolean someUserHasSeedAccount(in String accountName, in String accountType); boolean someUserHasAccount(in String accountName, in String accountType); boolean isProfile(int userId); boolean isManagedProfile(int userId); boolean isCloneProfile(int userId); Loading core/java/android/os/NewUserRequest.java +135 −7 Original line number Diff line number Diff line Loading @@ -17,7 +17,11 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.pm.UserInfo; import android.graphics.Bitmap; import android.text.TextUtils; /** * Contains necessary information to create user using Loading @@ -26,6 +30,7 @@ import android.annotation.SystemApi; * @hide */ @SystemApi @SuppressLint("PackageLayering") public final class NewUserRequest { @Nullable private final String mName; Loading @@ -33,16 +38,24 @@ public final class NewUserRequest { private final boolean mEphemeral; @NonNull private final String mUserType; private final Bitmap mUserIcon; private final String mAccountName; private final String mAccountType; private final PersistableBundle mAccountOptions; private NewUserRequest(Builder builder) { mName = builder.mName; mAdmin = builder.mAdmin; mEphemeral = builder.mEphemeral; mUserType = builder.mUserType; mUserIcon = builder.mUserIcon; mAccountName = builder.mAccountName; mAccountType = builder.mAccountType; mAccountOptions = builder.mAccountOptions; } /** * Gets the user name. * Returns the name of the user. */ @Nullable public String getName() { Loading @@ -50,7 +63,7 @@ public final class NewUserRequest { } /** * Is user Ephemenral? * Returns whether the user is ephemeral. * * <p> Ephemeral user will be removed after leaving the foreground. */ Loading @@ -59,7 +72,7 @@ public final class NewUserRequest { } /** * Is user Admin? * Returns whether the user is an admin. * * <p> Admin user is with administrative privileges and such user can create and * delete users. Loading @@ -69,7 +82,17 @@ public final class NewUserRequest { } /** * Gets user type. * Returns the calculated flags for user creation. */ int getFlags() { int flags = 0; if (isAdmin()) flags |= UserInfo.FLAG_ADMIN; if (isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL; return flags; } /** * Returns the user type. * * <p> Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and * {@link USER_TYPE_FULL_GUEST} Loading @@ -79,25 +102,71 @@ public final class NewUserRequest { return mUserType; } /** * Returns the user icon. */ @Nullable public Bitmap getUserIcon() { return mUserIcon; } /** * Returns the account name. */ @Nullable public String getAccountName() { return mAccountName; } /** * Returns the account type. */ @Nullable public String getAccountType() { return mAccountType; } /** * Returns the account options. */ @SuppressLint("NullableCollection") @Nullable public PersistableBundle getAccountOptions() { return mAccountOptions; } @Override public String toString() { return String.format( "NewUserRequest- UserName:%s, userType:%s, IsAdmin:%s, IsEphemeral:%s.", mName, mUserType, mAdmin, mEphemeral); return "NewUserRequest{" + "mName='" + mName + '\'' + ", mAdmin=" + mAdmin + ", mEphemeral=" + mEphemeral + ", mUserType='" + mUserType + '\'' + ", mAccountName='" + mAccountName + '\'' + ", mAccountType='" + mAccountType + '\'' + ", mAccountOptions=" + mAccountOptions + '}'; } /** * Builder for building {@link NewUserRequest} */ @SuppressLint("PackageLayering") public static final class Builder { private String mName; private boolean mAdmin; private boolean mEphemeral; private String mUserType = UserManager.USER_TYPE_FULL_SECONDARY; private Bitmap mUserIcon; private String mAccountName; private String mAccountType; private PersistableBundle mAccountOptions; /** * Sets user name. * * @return This object for method chaining. */ @NonNull public Builder setName(@Nullable String name) { Loading @@ -110,6 +179,8 @@ public final class NewUserRequest { * * <p> Admin user is with administrative privileges and such user can create * and delete users. * * @return This object for method chaining. */ @NonNull public Builder setAdmin() { Loading @@ -121,6 +192,8 @@ public final class NewUserRequest { * Sets user as ephemeral. * * <p> Ephemeral user will be removed after leaving the foreground. * * @return This object for method chaining. */ @NonNull public Builder setEphemeral() { Loading @@ -134,6 +207,8 @@ public final class NewUserRequest { * Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and * {@link UserManager.USER_TYPE_FULL_GUEST}. Default value is * {@link UserManager.USER_TYPE_FULL_SECONDARY}. * * @return This object for method chaining. */ @NonNull public Builder setUserType(@NonNull String type) { Loading @@ -141,6 +216,54 @@ public final class NewUserRequest { return this; } /** * Sets user icon. * * @return This object for method chaining. */ @NonNull public Builder setUserIcon(@Nullable Bitmap userIcon) { mUserIcon = userIcon; return this; } /** * Sets account name that will be used by the setup wizard to initialize the user. * * @see android.accounts.Account * @return This object for method chaining. */ @NonNull public Builder setAccountName(@Nullable String accountName) { mAccountName = accountName; return this; } /** * Sets account type for the account to be created. This is required if the account name * is not null. This will be used by the setup wizard to initialize the user. * * @see android.accounts.Account * @return This object for method chaining. */ @NonNull public Builder setAccountType(@Nullable String accountType) { mAccountType = accountType; return this; } /** * Sets account options that can contain account-specific extra information * to be used by setup wizard to initialize the account for the user. * * @return This object for method chaining. */ @NonNull public Builder setAccountOptions(@Nullable PersistableBundle accountOptions) { mAccountOptions = accountOptions; return this; } /** * Builds {@link NewUserRequest} * Loading @@ -165,6 +288,11 @@ public final class NewUserRequest { && mUserType != UserManager.USER_TYPE_FULL_GUEST) { throw new IllegalStateException("Unsupported user type: " + mUserType); } if (TextUtils.isEmpty(mAccountName) != TextUtils.isEmpty(mAccountType)) { throw new IllegalStateException( "Account name and account type should be provided together."); } } } } core/java/android/os/UserManager.java +50 −19 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; /** Loading Loading @@ -1660,6 +1661,14 @@ public class UserManager { */ public static final int USER_OPERATION_ERROR_MAX_USERS = 6; /** * Indicates user operation failed because a user with that account already exists. * * @hide */ @SystemApi public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7; /** * Result returned from various user operations. * Loading @@ -1673,7 +1682,8 @@ public class UserManager { USER_OPERATION_ERROR_MAX_RUNNING_USERS, USER_OPERATION_ERROR_CURRENT_USER, USER_OPERATION_ERROR_LOW_STORAGE, USER_OPERATION_ERROR_MAX_USERS USER_OPERATION_ERROR_MAX_USERS, USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS }) public @interface UserOperationResult {} Loading Loading @@ -3159,26 +3169,24 @@ public class UserManager { @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) public @NonNull NewUserResponse createUser(@NonNull NewUserRequest newUserRequest) { UserInfo user = null; int operationResult = USER_OPERATION_ERROR_UNKNOWN; try { user = createUser(newUserRequest.getName(), newUserRequest.getUserType(), determineFlagsForUserCreation(newUserRequest)); } catch (UserOperationException e) { final UserHandle userHandle = mService.createUserWithAttributes( newUserRequest.getName(), newUserRequest.getUserType(), newUserRequest.getFlags(), newUserRequest.getUserIcon(), newUserRequest.getAccountName(), newUserRequest.getAccountType(), newUserRequest.getAccountOptions()); return new NewUserResponse(userHandle, USER_OPERATION_SUCCESS); } catch (ServiceSpecificException e) { Log.w(TAG, "Exception while creating user " + newUserRequest, e); operationResult = e.getUserOperationResult(); } if (user == null) { return new NewUserResponse(null, operationResult); } return new NewUserResponse(user.getUserHandle(), USER_OPERATION_SUCCESS); return new NewUserResponse(null, e.errorCode); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } private int determineFlagsForUserCreation(NewUserRequest newUserRequest) { int flags = 0; if (newUserRequest.isAdmin()) flags |= UserInfo.FLAG_ADMIN; if (newUserRequest.isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL; return flags; } /** Loading Loading @@ -4913,12 +4921,12 @@ public class UserManager { } /** * @hide * Checks if any uninitialized user has the specific seed account name and type. * * @param accountName The account name to check for * @param accountType The account type of the account to check for * @return whether the seed account was found * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean someUserHasSeedAccount(String accountName, String accountType) { Loading @@ -4929,6 +4937,29 @@ public class UserManager { } } /** * Checks if any initialized or uninitialized user has the specific account name and type. * * @param accountName The account name to check for * @param accountType The account type of the account to check for * @return whether the account was found * @hide */ @SystemApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount( @NonNull String accountName, @NonNull String accountType) { Objects.requireNonNull(accountName, "accountName must not be null"); Objects.requireNonNull(accountType, "accountType must not be null"); try { return mService.someUserHasAccount(accountName, accountType); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * @hide * User that enforces a restriction. Loading services/core/java/com/android/server/pm/UserManagerService.java +68 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.ColorRes; import android.annotation.DrawableRes; import android.annotation.NonNull; Loading Loading @@ -85,6 +87,7 @@ import android.provider.Settings; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; Loading Loading @@ -3512,6 +3515,39 @@ public class UserManagerService extends IUserManager.Stub { } } @Override public UserHandle createUserWithAttributes( String userName, String userType, @UserInfoFlag int flags, Bitmap userIcon, String accountName, String accountType, PersistableBundle accountOptions) { checkManageOrCreateUsersPermission(flags); if (someUserHasAccountNoChecks(accountName, accountType)) { throw new ServiceSpecificException( UserManager.USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS); } UserInfo userInfo; try { userInfo = createUserInternal(userName, userType, flags, UserHandle.USER_NULL, null); if (userInfo == null) { throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN); } } catch (UserManager.CheckedUserOperationException e) { throw e.toServiceSpecificException(); } if (userIcon != null) { mLocalService.setUserIcon(userInfo.id, userIcon); } setSeedAccountDataNoChecks(userInfo.id, accountName, accountType, accountOptions, true); return userInfo.getUserHandle(); } private UserInfo createUserInternal(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId, @Nullable String[] disallowedPackages) Loading Loading @@ -4934,7 +4970,12 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setSeedAccountData(@UserIdInt int userId, String accountName, String accountType, PersistableBundle accountOptions, boolean persist) { checkManageUsersPermission("Require MANAGE_USERS permission to set user seed data"); checkManageUsersPermission("set user seed account data"); setSeedAccountDataNoChecks(userId, accountName, accountType, accountOptions, persist); } private void setSeedAccountDataNoChecks(@UserIdInt int userId, String accountName, String accountType, PersistableBundle accountOptions, boolean persist) { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { Loading Loading @@ -4996,14 +5037,18 @@ public class UserManagerService extends IUserManager.Stub { } @Override public boolean someUserHasSeedAccount(String accountName, String accountType) throws RemoteException { checkManageUsersPermission("Cannot check seed account information"); public boolean someUserHasSeedAccount(String accountName, String accountType) { checkManageUsersPermission("check seed account information"); return someUserHasSeedAccountNoChecks(accountName, accountType); } private boolean someUserHasSeedAccountNoChecks(String accountName, String accountType) { synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { final UserData data = mUsers.valueAt(i); if (data.info.isInitialized()) continue; if (mRemovingUserIds.get(data.info.id)) continue; if (data.seedAccountName == null || !data.seedAccountName.equals(accountName)) { continue; } Loading @@ -5016,6 +5061,25 @@ public class UserManagerService extends IUserManager.Stub { return false; } @Override public boolean someUserHasAccount(String accountName, String accountType) { checkManageOrCreateUsersPermission("check seed account information"); return someUserHasAccountNoChecks(accountName, accountType); } private boolean someUserHasAccountNoChecks( String accountName, String accountType) { if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { return false; } final Account account = new Account(accountName, accountType); return Binder.withCleanCallingIdentity(() -> AccountManager.get(mContext).someUserHasAccount(account) || someUserHasSeedAccountNoChecks(accountName, accountType)); } @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, Loading Loading
core/api/system-current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -8681,7 +8681,11 @@ package android.os { } public final class NewUserRequest { method @Nullable public String getAccountName(); method @Nullable public android.os.PersistableBundle getAccountOptions(); method @Nullable public String getAccountType(); method @Nullable public String getName(); method @Nullable public android.graphics.Bitmap getUserIcon(); method @NonNull public String getUserType(); method public boolean isAdmin(); method public boolean isEphemeral(); Loading @@ -8690,9 +8694,13 @@ package android.os { public static final class NewUserRequest.Builder { ctor public NewUserRequest.Builder(); method @NonNull public android.os.NewUserRequest build(); method @NonNull public android.os.NewUserRequest.Builder setAccountName(@Nullable String); method @NonNull public android.os.NewUserRequest.Builder setAccountOptions(@Nullable android.os.PersistableBundle); method @NonNull public android.os.NewUserRequest.Builder setAccountType(@Nullable String); method @NonNull public android.os.NewUserRequest.Builder setAdmin(); method @NonNull public android.os.NewUserRequest.Builder setEphemeral(); method @NonNull public android.os.NewUserRequest.Builder setName(@Nullable String); method @NonNull public android.os.NewUserRequest.Builder setUserIcon(@Nullable android.graphics.Bitmap); method @NonNull public android.os.NewUserRequest.Builder setUserType(@NonNull String); } Loading Loading @@ -8970,6 +8978,7 @@ package android.os { 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); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull 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"; field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; Loading @@ -8981,6 +8990,7 @@ package android.os { 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 int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7; // 0x7 field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST"; 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";
core/java/android/os/IUserManager.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ package android.os; import android.os.Bundle; import android.os.IUserRestrictionsListener; import android.os.PersistableBundle; import android.os.UserHandle; import android.os.UserManager; import android.content.pm.UserInfo; import android.content.IntentSender; Loading Loading @@ -91,6 +92,9 @@ interface IUserManager { boolean markGuestForDeletion(int userId); UserInfo findCurrentGuestUser(); boolean isQuietModeEnabled(int userId); UserHandle createUserWithAttributes(in String userName, in String userType, int flags, in Bitmap userIcon, in String accountName, in String accountType, in PersistableBundle accountOptions); void setSeedAccountData(int userId, in String accountName, in String accountType, in PersistableBundle accountOptions, boolean persist); String getSeedAccountName(int userId); Loading @@ -98,6 +102,7 @@ interface IUserManager { PersistableBundle getSeedAccountOptions(int userId); void clearSeedAccountData(int userId); boolean someUserHasSeedAccount(in String accountName, in String accountType); boolean someUserHasAccount(in String accountName, in String accountType); boolean isProfile(int userId); boolean isManagedProfile(int userId); boolean isCloneProfile(int userId); Loading
core/java/android/os/NewUserRequest.java +135 −7 Original line number Diff line number Diff line Loading @@ -17,7 +17,11 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.pm.UserInfo; import android.graphics.Bitmap; import android.text.TextUtils; /** * Contains necessary information to create user using Loading @@ -26,6 +30,7 @@ import android.annotation.SystemApi; * @hide */ @SystemApi @SuppressLint("PackageLayering") public final class NewUserRequest { @Nullable private final String mName; Loading @@ -33,16 +38,24 @@ public final class NewUserRequest { private final boolean mEphemeral; @NonNull private final String mUserType; private final Bitmap mUserIcon; private final String mAccountName; private final String mAccountType; private final PersistableBundle mAccountOptions; private NewUserRequest(Builder builder) { mName = builder.mName; mAdmin = builder.mAdmin; mEphemeral = builder.mEphemeral; mUserType = builder.mUserType; mUserIcon = builder.mUserIcon; mAccountName = builder.mAccountName; mAccountType = builder.mAccountType; mAccountOptions = builder.mAccountOptions; } /** * Gets the user name. * Returns the name of the user. */ @Nullable public String getName() { Loading @@ -50,7 +63,7 @@ public final class NewUserRequest { } /** * Is user Ephemenral? * Returns whether the user is ephemeral. * * <p> Ephemeral user will be removed after leaving the foreground. */ Loading @@ -59,7 +72,7 @@ public final class NewUserRequest { } /** * Is user Admin? * Returns whether the user is an admin. * * <p> Admin user is with administrative privileges and such user can create and * delete users. Loading @@ -69,7 +82,17 @@ public final class NewUserRequest { } /** * Gets user type. * Returns the calculated flags for user creation. */ int getFlags() { int flags = 0; if (isAdmin()) flags |= UserInfo.FLAG_ADMIN; if (isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL; return flags; } /** * Returns the user type. * * <p> Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and * {@link USER_TYPE_FULL_GUEST} Loading @@ -79,25 +102,71 @@ public final class NewUserRequest { return mUserType; } /** * Returns the user icon. */ @Nullable public Bitmap getUserIcon() { return mUserIcon; } /** * Returns the account name. */ @Nullable public String getAccountName() { return mAccountName; } /** * Returns the account type. */ @Nullable public String getAccountType() { return mAccountType; } /** * Returns the account options. */ @SuppressLint("NullableCollection") @Nullable public PersistableBundle getAccountOptions() { return mAccountOptions; } @Override public String toString() { return String.format( "NewUserRequest- UserName:%s, userType:%s, IsAdmin:%s, IsEphemeral:%s.", mName, mUserType, mAdmin, mEphemeral); return "NewUserRequest{" + "mName='" + mName + '\'' + ", mAdmin=" + mAdmin + ", mEphemeral=" + mEphemeral + ", mUserType='" + mUserType + '\'' + ", mAccountName='" + mAccountName + '\'' + ", mAccountType='" + mAccountType + '\'' + ", mAccountOptions=" + mAccountOptions + '}'; } /** * Builder for building {@link NewUserRequest} */ @SuppressLint("PackageLayering") public static final class Builder { private String mName; private boolean mAdmin; private boolean mEphemeral; private String mUserType = UserManager.USER_TYPE_FULL_SECONDARY; private Bitmap mUserIcon; private String mAccountName; private String mAccountType; private PersistableBundle mAccountOptions; /** * Sets user name. * * @return This object for method chaining. */ @NonNull public Builder setName(@Nullable String name) { Loading @@ -110,6 +179,8 @@ public final class NewUserRequest { * * <p> Admin user is with administrative privileges and such user can create * and delete users. * * @return This object for method chaining. */ @NonNull public Builder setAdmin() { Loading @@ -121,6 +192,8 @@ public final class NewUserRequest { * Sets user as ephemeral. * * <p> Ephemeral user will be removed after leaving the foreground. * * @return This object for method chaining. */ @NonNull public Builder setEphemeral() { Loading @@ -134,6 +207,8 @@ public final class NewUserRequest { * Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and * {@link UserManager.USER_TYPE_FULL_GUEST}. Default value is * {@link UserManager.USER_TYPE_FULL_SECONDARY}. * * @return This object for method chaining. */ @NonNull public Builder setUserType(@NonNull String type) { Loading @@ -141,6 +216,54 @@ public final class NewUserRequest { return this; } /** * Sets user icon. * * @return This object for method chaining. */ @NonNull public Builder setUserIcon(@Nullable Bitmap userIcon) { mUserIcon = userIcon; return this; } /** * Sets account name that will be used by the setup wizard to initialize the user. * * @see android.accounts.Account * @return This object for method chaining. */ @NonNull public Builder setAccountName(@Nullable String accountName) { mAccountName = accountName; return this; } /** * Sets account type for the account to be created. This is required if the account name * is not null. This will be used by the setup wizard to initialize the user. * * @see android.accounts.Account * @return This object for method chaining. */ @NonNull public Builder setAccountType(@Nullable String accountType) { mAccountType = accountType; return this; } /** * Sets account options that can contain account-specific extra information * to be used by setup wizard to initialize the account for the user. * * @return This object for method chaining. */ @NonNull public Builder setAccountOptions(@Nullable PersistableBundle accountOptions) { mAccountOptions = accountOptions; return this; } /** * Builds {@link NewUserRequest} * Loading @@ -165,6 +288,11 @@ public final class NewUserRequest { && mUserType != UserManager.USER_TYPE_FULL_GUEST) { throw new IllegalStateException("Unsupported user type: " + mUserType); } if (TextUtils.isEmpty(mAccountName) != TextUtils.isEmpty(mAccountType)) { throw new IllegalStateException( "Account name and account type should be provided together."); } } } }
core/java/android/os/UserManager.java +50 −19 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; /** Loading Loading @@ -1660,6 +1661,14 @@ public class UserManager { */ public static final int USER_OPERATION_ERROR_MAX_USERS = 6; /** * Indicates user operation failed because a user with that account already exists. * * @hide */ @SystemApi public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7; /** * Result returned from various user operations. * Loading @@ -1673,7 +1682,8 @@ public class UserManager { USER_OPERATION_ERROR_MAX_RUNNING_USERS, USER_OPERATION_ERROR_CURRENT_USER, USER_OPERATION_ERROR_LOW_STORAGE, USER_OPERATION_ERROR_MAX_USERS USER_OPERATION_ERROR_MAX_USERS, USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS }) public @interface UserOperationResult {} Loading Loading @@ -3159,26 +3169,24 @@ public class UserManager { @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) public @NonNull NewUserResponse createUser(@NonNull NewUserRequest newUserRequest) { UserInfo user = null; int operationResult = USER_OPERATION_ERROR_UNKNOWN; try { user = createUser(newUserRequest.getName(), newUserRequest.getUserType(), determineFlagsForUserCreation(newUserRequest)); } catch (UserOperationException e) { final UserHandle userHandle = mService.createUserWithAttributes( newUserRequest.getName(), newUserRequest.getUserType(), newUserRequest.getFlags(), newUserRequest.getUserIcon(), newUserRequest.getAccountName(), newUserRequest.getAccountType(), newUserRequest.getAccountOptions()); return new NewUserResponse(userHandle, USER_OPERATION_SUCCESS); } catch (ServiceSpecificException e) { Log.w(TAG, "Exception while creating user " + newUserRequest, e); operationResult = e.getUserOperationResult(); } if (user == null) { return new NewUserResponse(null, operationResult); } return new NewUserResponse(user.getUserHandle(), USER_OPERATION_SUCCESS); return new NewUserResponse(null, e.errorCode); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } private int determineFlagsForUserCreation(NewUserRequest newUserRequest) { int flags = 0; if (newUserRequest.isAdmin()) flags |= UserInfo.FLAG_ADMIN; if (newUserRequest.isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL; return flags; } /** Loading Loading @@ -4913,12 +4921,12 @@ public class UserManager { } /** * @hide * Checks if any uninitialized user has the specific seed account name and type. * * @param accountName The account name to check for * @param accountType The account type of the account to check for * @return whether the seed account was found * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean someUserHasSeedAccount(String accountName, String accountType) { Loading @@ -4929,6 +4937,29 @@ public class UserManager { } } /** * Checks if any initialized or uninitialized user has the specific account name and type. * * @param accountName The account name to check for * @param accountType The account type of the account to check for * @return whether the account was found * @hide */ @SystemApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount( @NonNull String accountName, @NonNull String accountType) { Objects.requireNonNull(accountName, "accountName must not be null"); Objects.requireNonNull(accountType, "accountType must not be null"); try { return mService.someUserHasAccount(accountName, accountType); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * @hide * User that enforces a restriction. Loading
services/core/java/com/android/server/pm/UserManagerService.java +68 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.ColorRes; import android.annotation.DrawableRes; import android.annotation.NonNull; Loading Loading @@ -85,6 +87,7 @@ import android.provider.Settings; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; Loading Loading @@ -3512,6 +3515,39 @@ public class UserManagerService extends IUserManager.Stub { } } @Override public UserHandle createUserWithAttributes( String userName, String userType, @UserInfoFlag int flags, Bitmap userIcon, String accountName, String accountType, PersistableBundle accountOptions) { checkManageOrCreateUsersPermission(flags); if (someUserHasAccountNoChecks(accountName, accountType)) { throw new ServiceSpecificException( UserManager.USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS); } UserInfo userInfo; try { userInfo = createUserInternal(userName, userType, flags, UserHandle.USER_NULL, null); if (userInfo == null) { throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN); } } catch (UserManager.CheckedUserOperationException e) { throw e.toServiceSpecificException(); } if (userIcon != null) { mLocalService.setUserIcon(userInfo.id, userIcon); } setSeedAccountDataNoChecks(userInfo.id, accountName, accountType, accountOptions, true); return userInfo.getUserHandle(); } private UserInfo createUserInternal(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId, @Nullable String[] disallowedPackages) Loading Loading @@ -4934,7 +4970,12 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setSeedAccountData(@UserIdInt int userId, String accountName, String accountType, PersistableBundle accountOptions, boolean persist) { checkManageUsersPermission("Require MANAGE_USERS permission to set user seed data"); checkManageUsersPermission("set user seed account data"); setSeedAccountDataNoChecks(userId, accountName, accountType, accountOptions, persist); } private void setSeedAccountDataNoChecks(@UserIdInt int userId, String accountName, String accountType, PersistableBundle accountOptions, boolean persist) { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { Loading Loading @@ -4996,14 +5037,18 @@ public class UserManagerService extends IUserManager.Stub { } @Override public boolean someUserHasSeedAccount(String accountName, String accountType) throws RemoteException { checkManageUsersPermission("Cannot check seed account information"); public boolean someUserHasSeedAccount(String accountName, String accountType) { checkManageUsersPermission("check seed account information"); return someUserHasSeedAccountNoChecks(accountName, accountType); } private boolean someUserHasSeedAccountNoChecks(String accountName, String accountType) { synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { final UserData data = mUsers.valueAt(i); if (data.info.isInitialized()) continue; if (mRemovingUserIds.get(data.info.id)) continue; if (data.seedAccountName == null || !data.seedAccountName.equals(accountName)) { continue; } Loading @@ -5016,6 +5061,25 @@ public class UserManagerService extends IUserManager.Stub { return false; } @Override public boolean someUserHasAccount(String accountName, String accountType) { checkManageOrCreateUsersPermission("check seed account information"); return someUserHasAccountNoChecks(accountName, accountType); } private boolean someUserHasAccountNoChecks( String accountName, String accountType) { if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { return false; } final Account account = new Account(accountName, accountType); return Binder.withCleanCallingIdentity(() -> AccountManager.get(mContext).someUserHasAccount(account) || someUserHasSeedAccountNoChecks(accountName, accountType)); } @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, Loading