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

Commit 44a18081 authored by Alan Treadway's avatar Alan Treadway Committed by Android (Google) Code Review
Browse files

Merge "Add explicit and persistent user provisioning state."

parents 65630250 afad8783
Loading
Loading
Loading
Loading
+10 −1
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.commands.dpm;


import android.app.ActivityManagerNative;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
@@ -143,6 +144,10 @@ public final class Dpm extends BaseCommand {
            mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
            mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
            throw e;
            throw e;
        }
        }

        mDevicePolicyManager.setUserProvisioningState(
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);

        System.out.println("Success: Device owner set to package " + mComponent);
        System.out.println("Success: Device owner set to package " + mComponent);
        System.out.println("Active admin set to component " + mComponent.toShortString());
        System.out.println("Active admin set to component " + mComponent.toShortString());
    }
    }
@@ -161,6 +166,10 @@ public final class Dpm extends BaseCommand {
            mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId);
            mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId);
            throw e;
            throw e;
        }
        }

        mDevicePolicyManager.setUserProvisioningState(
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);

        System.out.println("Success: Active admin and profile owner set to "
        System.out.println("Success: Active admin and profile owner set to "
                + mComponent.toShortString() + " for user " + mUserId);
                + mComponent.toShortString() + " for user " + mUserId);
    }
    }
+90 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.app.admin;
package android.app.admin;


import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant;
@@ -53,6 +54,8 @@ import org.xmlpull.v1.XmlPullParserException;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetSocketAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy;
import java.security.KeyFactory;
import java.security.KeyFactory;
@@ -279,6 +282,21 @@ public class DevicePolicyManager {
    public static final String ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
    public static final String ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
        = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";
        = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";


    /**
     * Activity action: Finalizes management provisioning, should be used after user-setup
     * has been completed and {@link #getUserProvisioningState()} returns one of:
     * <ul>
     * <li>{@link #STATE_USER_SETUP_INCOMPLETE}</li>
     * <li>{@link #STATE_USER_SETUP_COMPLETE}</li>
     * <li>{@link #STATE_USER_PROFILE_COMPLETE}</li>
     * </ul>
     *
     * @hide
     */
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    public static final String ACTION_PROVISION_FINALIZATION
            = "android.app.action.PROVISION_FINALIZATION";

    /**
    /**
     * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
     * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
     * allows a mobile device management application or NFC programmer application which starts
     * allows a mobile device management application or NFC programmer application which starts
@@ -860,6 +878,44 @@ public class DevicePolicyManager {
     */
     */
    public static final int PERMISSION_GRANT_STATE_DENIED = 2;
    public static final int PERMISSION_GRANT_STATE_DENIED = 2;


    /**
     * No management for current user in-effect. This is the default.
     * @hide
     */
    public static final int STATE_USER_UNMANAGED = 0;

    /**
     * Management partially setup, user setup needs to be completed.
     * @hide
     */
    public static final int STATE_USER_SETUP_INCOMPLETE = 1;

    /**
     * Management partially setup, user setup completed.
     * @hide
     */
    public static final int STATE_USER_SETUP_COMPLETE = 2;

    /**
     * Management setup and active on current user.
     * @hide
     */
    public static final int STATE_USER_SETUP_FINALIZED = 3;

    /**
     * Management partially setup on a managed profile.
     * @hide
     */
    public static final int STATE_USER_PROFILE_COMPLETE = 4;

    /**
     * @hide
     */
    @IntDef({STATE_USER_UNMANAGED, STATE_USER_SETUP_INCOMPLETE, STATE_USER_SETUP_COMPLETE,
            STATE_USER_SETUP_FINALIZED, STATE_USER_PROFILE_COMPLETE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface UserProvisioningState {}

    /**
    /**
     * Return true if the given administrator component is currently
     * Return true if the given administrator component is currently
     * active (enabled) in the system.
     * active (enabled) in the system.
@@ -5246,6 +5302,40 @@ public class DevicePolicyManager {
        }
        }
    }
    }


    /**
     * @return the {@link UserProvisioningState} for the current user - for unmanaged users will
     *         return {@link #STATE_USER_UNMANAGED}
     * @hide
     */
    @UserProvisioningState
    public int getUserProvisioningState() {
        if (mService != null) {
            try {
                return mService.getUserProvisioningState();
            } catch (RemoteException e) {
                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
            }
        }
        return STATE_USER_UNMANAGED;
    }

    /**
     * Set the {@link UserProvisioningState} for the supplied user, if they are managed.
     *
     * @param state to store
     * @param userHandle for user
     * @hide
     */
    public void setUserProvisioningState(@UserProvisioningState int state, int userHandle) {
        if (mService != null) {
            try {
                mService.setUserProvisioningState(state, userHandle);
            } catch (RemoteException e) {
                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
            }
        }
    }

    /**
    /**
     * @hide
     * @hide
     * Indicates the entity that controls the device or profile owner. A user/profile is considered
     * Indicates the entity that controls the device or profile owner. A user/profile is considered
+3 −0
Original line number Original line Diff line number Diff line
@@ -269,6 +269,9 @@ interface IDevicePolicyManager {
    int getOrganizationColor(in ComponentName admin);
    int getOrganizationColor(in ComponentName admin);
    int getOrganizationColorForUser(int userHandle);
    int getOrganizationColorForUser(int userHandle);


    int getUserProvisioningState();
    void setUserProvisioningState(int state, int userHandle);

    void setAffiliationIds(in ComponentName admin, in List<String> ids);
    void setAffiliationIds(in ComponentName admin, in List<String> ids);
    boolean isAffiliatedUser();
    boolean isAffiliatedUser();
}
}
+105 −1
Original line number Original line Diff line number Diff line
@@ -202,6 +202,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {


    private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
    private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
    private static final String ATTR_SETUP_COMPLETE = "setup-complete";
    private static final String ATTR_SETUP_COMPLETE = "setup-complete";
    private static final String ATTR_PROVISIONING_STATE = "provisioning-state";
    private static final String ATTR_PERMISSION_POLICY = "permission-policy";
    private static final String ATTR_PERMISSION_POLICY = "permission-policy";


    private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
    private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
@@ -364,6 +365,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        int mPasswordOwner = -1;
        int mPasswordOwner = -1;
        long mLastMaximumTimeToLock = -1;
        long mLastMaximumTimeToLock = -1;
        boolean mUserSetupComplete = false;
        boolean mUserSetupComplete = false;
        int mUserProvisioningState;
        int mPermissionPolicy;
        int mPermissionPolicy;


        final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
        final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
@@ -2017,6 +2019,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                out.attribute(null, ATTR_SETUP_COMPLETE,
                out.attribute(null, ATTR_SETUP_COMPLETE,
                        Boolean.toString(true));
                        Boolean.toString(true));
            }
            }
            if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
                out.attribute(null, ATTR_PROVISIONING_STATE,
                        Integer.toString(policy.mUserProvisioningState));
            }
            if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
            if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
                out.attribute(null, ATTR_PERMISSION_POLICY,
                out.attribute(null, ATTR_PERMISSION_POLICY,
                        Integer.toString(policy.mPermissionPolicy));
                        Integer.toString(policy.mPermissionPolicy));
@@ -2167,6 +2173,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
            if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
                policy.mUserSetupComplete = true;
                policy.mUserSetupComplete = true;
            }
            }
            String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
            if (!TextUtils.isEmpty(provisioningState)) {
                policy.mUserProvisioningState = Integer.parseInt(provisioningState);
            }
            String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
            String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
            if (!TextUtils.isEmpty(permissionPolicy)) {
            if (!TextUtils.isEmpty(permissionPolicy)) {
                policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
                policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
@@ -5379,6 +5389,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        policy.mDelegatedCertInstallerPackage = null;
        policy.mDelegatedCertInstallerPackage = null;
        policy.mApplicationRestrictionsManagingPackage = null;
        policy.mApplicationRestrictionsManagingPackage = null;
        policy.mStatusBarDisabled = false;
        policy.mStatusBarDisabled = false;
        policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
        saveSettingsLocked(userId);
        saveSettingsLocked(userId);


        final long ident = mInjector.binderClearCallingIdentity();
        final long ident = mInjector.binderClearCallingIdentity();
@@ -5405,6 +5416,98 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        return getUserData(userHandle).mUserSetupComplete;
        return getUserData(userHandle).mUserSetupComplete;
    }
    }


    @Override
    public int getUserProvisioningState() {
        if (!mHasFeature) {
            return DevicePolicyManager.STATE_USER_UNMANAGED;
        }
        int userHandle = mInjector.userHandleGetCallingUserId();
        return getUserProvisioningState(userHandle);
    }

    private int getUserProvisioningState(int userHandle) {
        return getUserData(userHandle).mUserProvisioningState;
    }

    @Override
    public void setUserProvisioningState(int newState, int userHandle) {
        if (!mHasFeature) {
            return;
        }

        if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle)
                && getManagedUserId(userHandle) == -1) {
            // No managed device, user or profile, so setting provisioning state makes no sense.
            throw new IllegalStateException("Not allowed to change provisioning state unless a "
                      + "device or profile owner is set.");
        }

        synchronized (this) {
            boolean transitionCheckNeeded = true;

            // Calling identity/permission checks.
            final int callingUid = mInjector.binderGetCallingUid();
            if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
                // ADB shell can only move directly from un-managed to finalized as part of directly
                // setting profile-owner or device-owner.
                if (getUserProvisioningState(userHandle) !=
                        DevicePolicyManager.STATE_USER_UNMANAGED
                        || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
                    throw new IllegalStateException("Not allowed to change provisioning state "
                            + "unless current provisioning state is unmanaged, and new state is "
                            + "finalized.");
                }
                transitionCheckNeeded = false;
            } else {
                // For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS.
                mContext.enforceCallingOrSelfPermission(
                        android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
            }

            final DevicePolicyData policyData = getUserData(userHandle);
            if (transitionCheckNeeded) {
                // Optional state transition check for non-ADB case.
                checkUserProvisioningStateTransition(policyData.mUserProvisioningState, newState);
            }
            policyData.mUserProvisioningState = newState;
            saveSettingsLocked(userHandle);
        }
    }

    private void checkUserProvisioningStateTransition(int currentState, int newState) {
        // Valid transitions for normal use-cases.
        switch (currentState) {
            case DevicePolicyManager.STATE_USER_UNMANAGED:
                // Can move to any state from unmanaged (except itself as an edge case)..
                if (newState != DevicePolicyManager.STATE_USER_UNMANAGED) {
                    return;
                }
                break;
            case DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE:
            case DevicePolicyManager.STATE_USER_SETUP_COMPLETE:
                // Can only move to finalized from these states.
                if (newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
                    return;
                }
                break;
            case DevicePolicyManager.STATE_USER_PROFILE_COMPLETE:
                // Current user has a managed-profile, but current user is not managed, so
                // rather than moving to finalized state, go back to unmanaged once
                // profile provisioning is complete.
                if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
                    return;
                }
                break;
            case DevicePolicyManager.STATE_USER_SETUP_FINALIZED:
                // Cannot transition out of finalized.
                break;
        }

        // Didn't meet any of the accepted state transition checks above, throw appropriate error.
        throw new IllegalStateException("Cannot move to user provisioning state [" + newState + "] "
                + "from state [" + currentState + "]");
    }

    @Override
    @Override
    public void setProfileEnabled(ComponentName who) {
    public void setProfileEnabled(ComponentName who) {
        if (!mHasFeature) {
        if (!mHasFeature) {
@@ -5724,7 +5827,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            for (int u = 0; u < userCount; u++) {
            for (int u = 0; u < userCount; u++) {
                DevicePolicyData policy = getUserData(mUserData.keyAt(u));
                DevicePolicyData policy = getUserData(mUserData.keyAt(u));
                pw.println();
                pw.println();
                pw.println("  Enabled Device Admins (User " + policy.mUserHandle + "):");
                pw.println("  Enabled Device Admins (User " + policy.mUserHandle
                        + ", provisioningState: " + policy.mUserProvisioningState + "):");
                final int N = policy.mAdminList.size();
                final int N = policy.mAdminList.size();
                for (int i=0; i<N; i++) {
                for (int i=0; i<N; i++) {
                    ActiveAdmin ap = policy.mAdminList.get(i);
                    ActiveAdmin ap = policy.mAdminList.get(i);
+162 −5
Original line number Original line Diff line number Diff line
@@ -15,9 +15,6 @@
 */
 */
package com.android.server.devicepolicy;
package com.android.server.devicepolicy;


import com.android.server.LocalServices;
import com.android.server.SystemService;

import android.Manifest.permission;
import android.Manifest.permission;
import android.app.Activity;
import android.app.Activity;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DeviceAdminReceiver;
@@ -27,7 +24,6 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Process;
import android.os.Process;
@@ -38,11 +34,16 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Pair;


import com.android.server.LocalServices;
import com.android.server.SystemService;

import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.Answer;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashMap;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
@@ -65,7 +66,7 @@ import static org.mockito.Mockito.when;
 *
 *
 m FrameworksServicesTests &&
 m FrameworksServicesTests &&
 adb install \
 adb install \
   -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
   -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
 adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
 adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
   -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
   -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner


@@ -73,6 +74,10 @@ import static org.mockito.Mockito.when;
 */
 */
@SmallTest
@SmallTest
public class DevicePolicyManagerTest extends DpmTestBase {
public class DevicePolicyManagerTest extends DpmTestBase {
    private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
            permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
            permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);

    private DpmMockContext mContext;
    private DpmMockContext mContext;
    public DevicePolicyManager dpm;
    public DevicePolicyManager dpm;
    public DevicePolicyManagerServiceTestable dpms;
    public DevicePolicyManagerServiceTestable dpms;
@@ -1545,4 +1550,156 @@ public class DevicePolicyManagerTest extends DpmTestBase {
        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
        assertTrue(dpm.isAffiliatedUser());
        assertTrue(dpm.isAffiliatedUser());
    }
    }

    public void testGetUserProvisioningState_defaultResult() {
        assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
    }

    public void testSetUserProvisioningState_permission() throws Exception {
        setupProfileOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
    }

    public void testSetUserProvisioningState_unprivileged() throws Exception {
        setupProfileOwner();
        try {
            dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
                    DpmMockContext.CALLER_USER_HANDLE);
            fail("Expected SecurityException");
        } catch (SecurityException expected) {
        }
    }

    public void testSetUserProvisioningState_noManagement() {
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
        try {
            dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
                    DpmMockContext.CALLER_USER_HANDLE);
            fail("IllegalStateException expected");
        } catch (IllegalStateException e) {
            MoreAsserts.assertContainsRegex("change provisioning state unless a .* owner is set",
                    e.getMessage());
        }
        assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
    }

    public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception {
        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
        setupDeviceOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
    }

    public void testSetUserProvisioningState_deviceOwnerFromSetupWizardAlternative()
            throws Exception {
        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
        setupDeviceOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
    }

    public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception {
        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
        setupDeviceOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
    }

    public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser()
            throws Exception {
        setupProfileOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
                DevicePolicyManager.STATE_USER_UNMANAGED);
    }

    public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
            throws Exception {
        setupProfileOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
    }

    public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception {
        setupProfileOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
    }

    public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception {
        setupProfileOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        try {
            exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                    DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
                    DevicePolicyManager.STATE_USER_UNMANAGED);
            fail("Expected IllegalStateException");
        } catch (IllegalStateException e) {
            MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
                    e.getMessage());
        }
    }

    public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
            throws Exception {
        setupProfileOwner();
        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);

        try {
            exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                    DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
                    DevicePolicyManager.STATE_USER_SETUP_COMPLETE);
            fail("Expected IllegalStateException");
        } catch (IllegalStateException e) {
            MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
                    e.getMessage());
        }
    }

    private void exerciseUserProvisioningTransitions(int userId, int... states) {
        assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
        for (int state : states) {
            dpm.setUserProvisioningState(state, userId);
            assertEquals(state, dpm.getUserProvisioningState());
        }
    }

    private void setupProfileOwner() throws Exception {
        mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);

        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
        dpm.setActiveAdmin(admin1, false);
        assertTrue(dpm.setProfileOwner(admin1, null, DpmMockContext.CALLER_USER_HANDLE));

        mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
    }

    private void setupDeviceOwner() throws Exception {
        mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);

        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
        dpm.setActiveAdmin(admin1, false);
        assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));

        mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
    }
}
}