Add new API function createAndManageUser

This is a reduced version of the (deprecated) function
createAndInitializeUser, that allows the device owner to create a
new user and pass a bundle with information for initialization. The
new version of the function has the same functionality, but the
profile owner of the new user is always the device owner.

A flag can be specified to skip the setup wizard for the new user.

The new user is not started in the background, as opposed to how
createAndInitializeUser did it. Instead, the bundle with
initialization information is stored and will be broadcast when the
user is started for the first time.

Bug: 25288732, 25860170
Change-Id: I4e1aea6d2b7821b412c131e88454dff5934192aa
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 53f7b29..9fe0ec4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4120,6 +4120,47 @@
     }
 
     /**
+      * Flag used by {@link createAndManageUser} to skip setup wizard after creating a new user.
+      * @hide
+      */
+    public static final int SKIP_SETUP_WIZARD = 0x0001;
+
+    /**
+     * Called by a device owner to create a user with the specified name and the caller as profile
+     * owner. The UserHandle returned by this method should not be persisted as user handles are
+     * recycled as users are removed and created. If you need to persist an identifier for this
+     * user, use {@link UserManager#getSerialNumberForUser}. The new user will not be started in the
+     * background.
+     *
+     * <p> admin is the {@link DeviceAdminReceiver} which is the device owner, and will become the
+     * profile owner and will be registered as an active admin on the new user. The profile owner
+     * package will be installed on the new user.
+     *
+     * <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.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param name The user's name.
+     * @param adminExtras Extras that will be passed to onEnable of the admin receiver on the new
+     *      user.
+     * @param flags {@link SKIP_SETUP_WIZARD} is supported.
+     * @see UserHandle
+     * @return the {@link android.os.UserHandle} object for the created user, or {@code null} if the
+     *         user could not be created.
+     * @hide
+     */
+    public UserHandle createAndManageUser(@NonNull ComponentName admin, @NonNull String name,
+            @Nullable PersistableBundle adminExtras, int flags) {
+        try {
+            return mService.createAndManageUser(admin, name, adminExtras, flags);
+        } catch (RemoteException re) {
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+        }
+        return null;
+    }
+
+    /**
      * Called by a device owner to remove a user and all associated data. The primary user can
      * not be removed.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 57865f4..e4ce4cd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -179,6 +179,7 @@
 
     UserHandle createUser(in ComponentName who, in String name);
     UserHandle createAndInitializeUser(in ComponentName who, in String name, in String profileOwnerName, in ComponentName profileOwnerComponent, in Bundle adminExtras);
+    UserHandle createAndManageUser(in ComponentName who, in String name, in PersistableBundle adminExtras, in int flags);
     boolean removeUser(in ComponentName who, in UserHandle userHandle);
     boolean switchUser(in ComponentName who, in UserHandle userHandle);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7232562..3a7c809 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -176,13 +176,19 @@
 
     private static final String TAG_STATUS_BAR = "statusbar";
 
-    private static final String TAG_AFFILIATION_ID = "affiliation-id";
-
     private static final String ATTR_DISABLED = "disabled";
 
     private static final String DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML =
             "do-not-ask-credentials-on-boot";
 
+    private static final String TAG_AFFILIATION_ID = "affiliation-id";
+
+    private static final String TAG_ADMIN_BROADCAST_PENDING = "admin-broadcast-pending";
+
+    private static final String ATTR_VALUE = "value";
+
+    private static final String TAG_INITIALIZATION_BUNDLE = "initialization-bundle";
+
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final long MS_PER_DAY = 86400 * 1000;
@@ -376,6 +382,10 @@
 
         Set<String> mAffiliationIds = new ArraySet<>();
 
+        // Used for initialization of users created by createAndManageUsers.
+        boolean mAdminBroadcastPending = false;
+        PersistableBundle mInitBundle = null;
+
         public DevicePolicyData(int userHandle) {
             mUserHandle = userHandle;
         }
@@ -424,16 +434,15 @@
             }
             if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 removeUserData(userHandle);
-            } else if (Intent.ACTION_USER_STARTED.equals(action)
-                    || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-
-                if (Intent.ACTION_USER_STARTED.equals(action)) {
+            } else if (Intent.ACTION_USER_STARTED.equals(action)) {
+                synchronized (DevicePolicyManagerService.this) {
                     // Reset the policy data
-                    synchronized (DevicePolicyManagerService.this) {
-                        mUserData.remove(userHandle);
-                    }
+                    mUserData.remove(userHandle);
+                    sendAdminEnabledBroadcastLocked(userHandle);
                 }
                 handlePackagesChanged(null /* check all admins */, userHandle);
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                handlePackagesChanged(null /* check all admins */, userHandle);
             } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
                     || (Intent.ACTION_PACKAGE_ADDED.equals(action)
                             && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) {
@@ -2036,6 +2045,19 @@
                 out.endTag(null, TAG_AFFILIATION_ID);
             }
 
+            if (policy.mAdminBroadcastPending) {
+                out.startTag(null, TAG_ADMIN_BROADCAST_PENDING);
+                out.attribute(null, ATTR_VALUE,
+                        Boolean.toString(policy.mAdminBroadcastPending));
+                out.endTag(null, TAG_ADMIN_BROADCAST_PENDING);
+            }
+
+            if (policy.mInitBundle != null) {
+                out.startTag(null, TAG_INITIALIZATION_BUNDLE);
+                policy.mInitBundle.saveToXml(out);
+                out.endTag(null, TAG_INITIALIZATION_BUNDLE);
+            }
+
             out.endTag(null, "policies");
 
             out.endDocument();
@@ -2044,7 +2066,7 @@
             stream.close();
             journal.commit();
             sendChangedNotification(userHandle);
-        } catch (IOException e) {
+        } catch (XmlPullParserException | IOException e) {
             Slog.w(LOG_TAG, "failed writing file", e);
             try {
                 if (stream != null) {
@@ -2170,6 +2192,11 @@
                     policy.doNotAskCredentialsOnBoot = true;
                 } else if (TAG_AFFILIATION_ID.equals(tag)) {
                     policy.mAffiliationIds.add(parser.getAttributeValue(null, "id"));
+                } else if (TAG_ADMIN_BROADCAST_PENDING.equals(tag)) {
+                    String pending = parser.getAttributeValue(null, ATTR_VALUE);
+                    policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
+                } else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) {
+                    policy.mInitBundle = PersistableBundle.restoreFromXml(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -6274,6 +6301,85 @@
         }
     }
 
+    private void sendAdminEnabledBroadcastLocked(int userHandle) {
+        DevicePolicyData policyData = getUserData(userHandle);
+        if (policyData.mAdminBroadcastPending) {
+            // Send the initialization data to profile owner and delete the data
+            ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+            if (admin != null) {
+                PersistableBundle initBundle = policyData.mInitBundle;
+                sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
+                        initBundle == null ? null : new Bundle(initBundle), null);
+            }
+            policyData.mInitBundle = null;
+            policyData.mAdminBroadcastPending = false;
+            saveSettingsLocked(userHandle);
+        }
+    }
+
+    @Override
+    public UserHandle createAndManageUser(ComponentName admin, String name,
+            PersistableBundle adminExtras, int flags) {
+        // Create user.
+        Preconditions.checkNotNull(admin, "ComponentName is null");
+        UserHandle user = null;
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+            final long id = mInjector.binderClearCallingIdentity();
+            try {
+                UserInfo userInfo = mUserManager.createUser(name, 0 /* flags */);
+                if (userInfo != null) {
+                    user = userInfo.getUserHandle();
+                }
+            } finally {
+                mInjector.binderRestoreCallingIdentity(id);
+            }
+        }
+        if (user == null) {
+            return null;
+        }
+        // Set admin.
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            final String adminPkg = admin.getPackageName();
+
+            final int userHandle = user.getIdentifier();
+            try {
+                // Install the profile owner if not present.
+                if (!mIPackageManager.isPackageAvailable(adminPkg, userHandle)) {
+                    mIPackageManager.installExistingPackageAsUser(adminPkg, userHandle);
+                }
+            } catch (RemoteException e) {
+                Slog.e(LOG_TAG, "Failed to make remote calls for createAndManageUser, "
+                        + "removing created user", e);
+                mUserManager.removeUser(user.getIdentifier());
+                return null;
+            }
+
+            setActiveAdmin(admin, true, userHandle);
+            // User is not started yet, the broadcast by setActiveAdmin will not be received.
+            // So we store adminExtras for broadcasting when the user starts for first time.
+            synchronized(this) {
+                DevicePolicyData policyData = getUserData(userHandle);
+                policyData.mInitBundle = adminExtras;
+                policyData.mAdminBroadcastPending = true;
+                saveSettingsLocked(userHandle);
+            }
+            final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier());
+            setProfileOwner(admin, ownerName, userHandle);
+
+            if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) {
+                Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle);
+            }
+
+            return user;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
     @Override
     public boolean removeUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");