Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7b8b6ac authored by Yasin Kilicdere's avatar Yasin Kilicdere Committed by Android (Google) Code Review
Browse files

Merge "Change createUser API in UserManager to create a user with seed account data."

parents 164fd183 d4a6b38c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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();
@@ -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);
  }
@@ -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";
@@ -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";
+5 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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);
+135 −7
Original line number Diff line number Diff line
@@ -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
@@ -26,6 +30,7 @@ import android.annotation.SystemApi;
 * @hide
 */
@SystemApi
@SuppressLint("PackageLayering")
public final class NewUserRequest {
    @Nullable
    private final String mName;
@@ -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() {
@@ -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.
     */
@@ -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.
@@ -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}
@@ -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) {
@@ -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() {
@@ -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() {
@@ -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) {
@@ -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}
         *
@@ -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.");
            }
        }
    }
}
+50 −19
Original line number Diff line number Diff line
@@ -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;

/**
@@ -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.
     *
@@ -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 {}

@@ -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;
    }

    /**
@@ -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) {
@@ -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.
+68 −4
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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)
@@ -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) {
@@ -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;
                }
@@ -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,