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

Commit 89386bac authored by Alex Chau's avatar Alex Chau
Browse files

createAndManageUser should communicate reason of failure to caller

Bug: 71844474
Test: cts-tradefed run singleCommand cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testCreateAndManageUser_LowStorage
Test: cts-tradefed run singleCommand cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testCreateAndManageUser_MaxUsers
Change-Id: I3c069ba86822178fa3f51f1d31cd4792883151cc
parent be012484
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -6702,11 +6702,6 @@ package android.app.admin {
    field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
    field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
    field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
    field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
    field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2
    field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3
    field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
    field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
    field public static final int WIPE_EUICC = 4; // 0x4
    field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
    field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -33165,6 +33160,17 @@ package android.os {
    field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
    field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
    field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
    field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
    field public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5; // 0x5
    field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2
    field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3
    field public static final int USER_OPERATION_ERROR_MAX_USERS = 6; // 0x6
    field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
    field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
  }
  public static class UserManager.UserOperationException extends java.lang.RuntimeException {
    method public int getUserOperationResult();
  }
  public abstract class VibrationEffect implements android.os.Parcelable {
+23 −74
Original line number Diff line number Diff line
@@ -56,8 +56,11 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.UserOperationException;
import android.os.UserManager.UserOperationResult;
import android.provider.ContactsContract.Directory;
import android.provider.Settings;
import android.security.AttestedKeyPair;
@@ -6549,6 +6552,9 @@ public class DevicePolicyManager {
     * <p>
     * If the adminExtras are not null, they will be stored on the device until the user is started
     * for the first time. Then the extras will be passed to the admin when onEnable is called.
     * <p>From {@link android.os.Build.VERSION_CODES#P} onwards, if targeting
     * {@link android.os.Build.VERSION_CODES#P}, throws {@link UserOperationException} instead of
     * returning {@code null} on failure.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param name The user's name.
@@ -6563,6 +6569,9 @@ public class DevicePolicyManager {
     * @return the {@link android.os.UserHandle} object for the created user, or {@code null} if the
     *         user could not be created.
     * @throws SecurityException if {@code admin} is not a device owner.
     * @throws UserOperationException if the user could not be created and the calling app is
     * targeting {@link android.os.Build.VERSION_CODES#P} and running on
     * {@link android.os.Build.VERSION_CODES#P}.
     */
    public @Nullable UserHandle createAndManageUser(@NonNull ComponentName admin,
            @NonNull String name,
@@ -6571,6 +6580,8 @@ public class DevicePolicyManager {
        throwIfParentInstance("createAndManageUser");
        try {
            return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
        } catch (ServiceSpecificException e) {
            throw new UserOperationException(e.getMessage(), e.errorCode);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
@@ -6613,78 +6624,16 @@ public class DevicePolicyManager {
        }
    }

    /**
     * Indicates user operation is successful.
     *
     * @see #startUserInBackground(ComponentName, UserHandle)
     * @see #stopUser(ComponentName, UserHandle)
     * @see #logoutUser(ComponentName)
     */
    public static final int USER_OPERATION_SUCCESS = 0;

    /**
     * Indicates user operation failed for unknown reason.
     *
     * @see #startUserInBackground(ComponentName, UserHandle)
     * @see #stopUser(ComponentName, UserHandle)
     * @see #logoutUser(ComponentName)
     */
    public static final int USER_OPERATION_ERROR_UNKNOWN = 1;

    /**
     * Indicates user operation failed because target user is a managed profile.
     *
     * @see #startUserInBackground(ComponentName, UserHandle)
     * @see #stopUser(ComponentName, UserHandle)
     * @see #logoutUser(ComponentName)
     */
    public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;

    /**
     * Indicates user operation failed because maximum running user limit has reached.
     *
     * @see #startUserInBackground(ComponentName, UserHandle)
     */
    public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;

    /**
     * Indicates user operation failed because the target user is in foreground.
     *
     * @see #stopUser(ComponentName, UserHandle)
     * @see #logoutUser(ComponentName)
     */
    public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;

    /**
     * Result returned from
     * <ul>
     * <li>{@link #startUserInBackground(ComponentName, UserHandle)}</li>
     * <li>{@link #stopUser(ComponentName, UserHandle)}</li>
     * <li>{@link #logoutUser(ComponentName)}</li>
     * </ul>
     *
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "USER_OPERATION_" }, value = {
            USER_OPERATION_SUCCESS,
            USER_OPERATION_ERROR_UNKNOWN,
            USER_OPERATION_ERROR_MANAGED_PROFILE,
            USER_OPERATION_ERROR_MAX_RUNNING_USERS,
            USER_OPERATION_ERROR_CURRENT_USER
    })
    public @interface UserOperationResult {}

    /**
     * Called by a device owner to start the specified secondary user in background.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param userHandle the user to be started in background.
     * @return one of the following result codes:
     * {@link #USER_OPERATION_ERROR_UNKNOWN},
     * {@link #USER_OPERATION_SUCCESS},
     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
     * {@link #USER_OPERATION_ERROR_MAX_RUNNING_USERS},
     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
     * {@link UserManager#USER_OPERATION_SUCCESS},
     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
     * {@link UserManager#USER_OPERATION_ERROR_MAX_RUNNING_USERS},
     * @throws SecurityException if {@code admin} is not a device owner.
     * @see #getSecondaryUsers(ComponentName)
     */
@@ -6704,10 +6653,10 @@ public class DevicePolicyManager {
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param userHandle the user to be stopped.
     * @return one of the following result codes:
     * {@link #USER_OPERATION_ERROR_UNKNOWN},
     * {@link #USER_OPERATION_SUCCESS},
     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
     * {@link #USER_OPERATION_ERROR_CURRENT_USER}
     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
     * {@link UserManager#USER_OPERATION_SUCCESS},
     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
     * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
     * @throws SecurityException if {@code admin} is not a device owner.
     * @see #getSecondaryUsers(ComponentName)
     */
@@ -6727,10 +6676,10 @@ public class DevicePolicyManager {
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @return one of the following result codes:
     * {@link #USER_OPERATION_ERROR_UNKNOWN},
     * {@link #USER_OPERATION_SUCCESS},
     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
     * {@link #USER_OPERATION_ERROR_CURRENT_USER}
     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
     * {@link UserManager#USER_OPERATION_SUCCESS},
     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
     * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
     * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device.
     * @see #getSecondaryUsers(ComponentName)
     */
+79 −0
Original line number Diff line number Diff line
@@ -1039,6 +1039,85 @@ public class UserManager {
     */
    public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1;

    /**
     * Indicates user operation is successful.
     */
    public static final int USER_OPERATION_SUCCESS = 0;

    /**
     * Indicates user operation failed for unknown reason.
     */
    public static final int USER_OPERATION_ERROR_UNKNOWN = 1;

    /**
     * Indicates user operation failed because target user is a managed profile.
     */
    public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;

    /**
     * Indicates user operation failed because maximum running user limit has been reached.
     */
    public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;

    /**
     * Indicates user operation failed because the target user is in the foreground.
     */
    public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;

    /**
     * Indicates user operation failed because device has low data storage.
     */
    public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5;

    /**
     * Indicates user operation failed because maximum user limit has been reached.
     */
    public static final int USER_OPERATION_ERROR_MAX_USERS = 6;

    /**
     * Result returned from various user operations.
     *
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "USER_OPERATION_" }, value = {
            USER_OPERATION_SUCCESS,
            USER_OPERATION_ERROR_UNKNOWN,
            USER_OPERATION_ERROR_MANAGED_PROFILE,
            USER_OPERATION_ERROR_MAX_RUNNING_USERS,
            USER_OPERATION_ERROR_CURRENT_USER,
            USER_OPERATION_ERROR_LOW_STORAGE,
            USER_OPERATION_ERROR_MAX_USERS
    })
    public @interface UserOperationResult {}

    /**
     * Thrown to indicate user operation failed.
     */
    public static class UserOperationException extends RuntimeException {
        private final @UserOperationResult int mUserOperationResult;

        /**
         * Constructs a UserOperationException with specific result code.
         *
         * @param message the detail message
         * @param userOperationResult the result code
         * @hide
         */
        public UserOperationException(String message,
                @UserOperationResult int userOperationResult) {
            super(message);
            mUserOperationResult = userOperationResult;
        }

        /**
         * Returns the operation result code.
         */
        public @UserOperationResult int getUserOperationResult() {
            return mUserOperationResult;
        }
    }

    /** @hide */
    public static UserManager get(Context context) {
        return (UserManager) context.getSystemService(Context.USER_SERVICE);
+54 −15
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@ import android.os.RecoverySystem;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -219,6 +220,7 @@ import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.google.android.collect.Sets;
@@ -8873,13 +8875,40 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
                && UserManager.isDeviceInDemoMode(mContext);
        final boolean leaveAllSystemAppsEnabled = (flags & LEAVE_ALL_SYSTEM_APPS_ENABLED) != 0;
        final int targetSdkVersion;
        // Create user.
        UserHandle user = null;
        synchronized (this) {
            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
            final int callingUid = mInjector.binderGetCallingUid();
            final long id = mInjector.binderClearCallingIdentity();
            try {
                targetSdkVersion = mInjector.getPackageManagerInternal().getUidTargetSdkVersion(
                        callingUid);
                // Return detail error code for checks inside
                // UserManagerService.createUserInternalUnchecked.
                DeviceStorageMonitorInternal deviceStorageMonitorInternal =
                        LocalServices.getService(DeviceStorageMonitorInternal.class);
                if (deviceStorageMonitorInternal.isMemoryLow()) {
                    if (targetSdkVersion >= Build.VERSION_CODES.P) {
                        throw new ServiceSpecificException(
                                UserManager.USER_OPERATION_ERROR_LOW_STORAGE, "low device storage");
                    } else {
                        return null;
                    }
                }
                if (!mUserManager.canAddMoreUsers()) {
                    if (targetSdkVersion >= Build.VERSION_CODES.P) {
                        throw new ServiceSpecificException(
                                UserManager.USER_OPERATION_ERROR_MAX_USERS, "user limit reached");
                    } else {
                        return null;
                    }
                }
                int userInfoFlags = 0;
                if (ephemeral) {
                    userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
@@ -8903,8 +8932,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            }
        }
        if (user == null) {
            if (targetSdkVersion >= Build.VERSION_CODES.P) {
                throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN,
                        "failed to create user");
            } else {
                return null;
            }
        }
        final int userHandle = user.getIdentifier();
        final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_USER_CREATED)
@@ -8949,7 +8983,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            return user;
        } catch (Throwable re) {
            mUserManager.removeUser(userHandle);
            if (targetSdkVersion >= Build.VERSION_CODES.P) {
                throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN,
                        re.getMessage());
            } else {
                return null;
            }
        } finally {
            mInjector.binderRestoreCallingIdentity(id);
        }
@@ -9030,24 +9069,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        final int userId = userHandle.getIdentifier();
        if (isManagedProfile(userId)) {
            Log.w(LOG_TAG, "Managed profile cannot be started in background");
            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
            return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
        }
        final long id = mInjector.binderClearCallingIdentity();
        try {
            if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
                Log.w(LOG_TAG, "Cannot start more users in background");
                return DevicePolicyManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
                return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
            }
            if (mInjector.getIActivityManager().startUserInBackground(userId)) {
                return DevicePolicyManager.USER_OPERATION_SUCCESS;
                return UserManager.USER_OPERATION_SUCCESS;
            } else {
                return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
                return UserManager.USER_OPERATION_ERROR_UNKNOWN;
            }
        } catch (RemoteException e) {
            // Same process, should not happen.
            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
            return UserManager.USER_OPERATION_ERROR_UNKNOWN;
        } finally {
            mInjector.binderRestoreCallingIdentity(id);
        }
@@ -9065,7 +9104,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        final int userId = userHandle.getIdentifier();
        if (isManagedProfile(userId)) {
            Log.w(LOG_TAG, "Managed profile cannot be stopped");
            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
            return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
        }
        return stopUserUnchecked(userId);
@@ -9086,7 +9125,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        if (isManagedProfile(callingUserId)) {
            Log.w(LOG_TAG, "Managed profile cannot be logout");
            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
            return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
        }
        final long id = mInjector.binderClearCallingIdentity();
@@ -9094,11 +9133,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
                Log.w(LOG_TAG, "Failed to switch to primary user");
                // This should never happen as target user is UserHandle.USER_SYSTEM
                return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
                return UserManager.USER_OPERATION_ERROR_UNKNOWN;
            }
        } catch (RemoteException e) {
            // Same process, should not happen.
            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
            return UserManager.USER_OPERATION_ERROR_UNKNOWN;
        } finally {
            mInjector.binderRestoreCallingIdentity(id);
        }
@@ -9111,15 +9150,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        try {
            switch (mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)) {
                case ActivityManager.USER_OP_SUCCESS:
                    return DevicePolicyManager.USER_OPERATION_SUCCESS;
                    return UserManager.USER_OPERATION_SUCCESS;
                case ActivityManager.USER_OP_IS_CURRENT:
                    return DevicePolicyManager.USER_OPERATION_ERROR_CURRENT_USER;
                    return UserManager.USER_OPERATION_ERROR_CURRENT_USER;
                default:
                    return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
                    return UserManager.USER_OPERATION_ERROR_UNKNOWN;
            }
        } catch (RemoteException e) {
            // Same process, should not happen.
            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
            return UserManager.USER_OPERATION_ERROR_UNKNOWN;
        } finally {
            mInjector.binderRestoreCallingIdentity(id);
        }