diff options
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"); |