summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java41
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java126
3 files changed, 158 insertions, 10 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 53f7b295f2ad..9fe0ec4f380e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4120,6 +4120,47 @@ public class DevicePolicyManager {
}
/**
+ * 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 57865f4c4d6c..e4ce4cd85afb 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -179,6 +179,7 @@ interface IDevicePolicyManager {
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 7232562e9669..3a7c809a05f9 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ 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");