From d14a0c4fdab119b24820936b0397a23955506db5 Mon Sep 17 00:00:00 2001 From: Ilyas Sung <ilyassung@google.com> Date: Tue, 28 Nov 2023 14:00:34 +0000 Subject: [PATCH] Headless DO implementation Bug: 289515470 Test: m Change-Id: I26721dfb1f53bdde4eb122fe0c545e76b40f5677 --- AconfigFlags.bp | 7 ++ core/api/current.txt | 1 + core/api/system-current.txt | 1 + .../android/app/admin/DeviceAdminInfo.java | 17 ++++- .../app/admin/DevicePolicyManager.java | 33 +++++++-- .../android/app/admin/flags/flags.aconfig | 7 ++ .../DevicePolicyManagerService.java | 74 +++++++++++++++---- 7 files changed, 118 insertions(+), 22 deletions(-) diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 1c6df75a4f02..79b4fd6ee9ae 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -855,6 +855,13 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +java_aconfig_library { + name: "device_policy_aconfig_flags_lib_host", + aconfig_declarations: "device_policy_aconfig_flags", + host_supported: true, + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + cc_aconfig_library { name: "device_policy_aconfig_flags_c_lib", aconfig_declarations: "device_policy_aconfig_flags", diff --git a/core/api/current.txt b/core/api/current.txt index 3fde9a69c5fb..b44307316f99 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7817,6 +7817,7 @@ package android.app.admin { method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DeviceAdminInfo> CREATOR; field public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1; // 0x1 + field @FlaggedApi("android.app.admin.flags.headless_device_owner_single_user_enabled") public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2; // 0x2 field public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0; // 0x0 field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7 field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c09653ded9f0..87307775d297 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1389,6 +1389,7 @@ package android.app.admin { field public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd field public static final int STATUS_HAS_DEVICE_OWNER = 1; // 0x1 field public static final int STATUS_HAS_PAIRED = 8; // 0x8 + field @FlaggedApi("android.app.admin.flags.headless_device_owner_single_user_enabled") public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17; // 0x11 field public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16; // 0x10 field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9 field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5 diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 14462b853c02..7d5d5c162271 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -16,8 +16,10 @@ package android.app.admin; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; +import android.app.admin.flags.Flags; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -176,7 +178,18 @@ public final class DeviceAdminInfo implements Parcelable { */ public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1; - @IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED}) + /** + * Value for {@link #getHeadlessDeviceOwnerMode} which indicates that this DPC should be + * provisioned into the first secondary user when on a Headless System User Mode device. + * + * <p>This mode only allows a single secondary user on the device blocking the creation of + * additional secondary users. + */ + @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED) + public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2; + + @IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED, + HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER}) @Retention(RetentionPolicy.SOURCE) private @interface HeadlessDeviceOwnerMode {} @@ -373,6 +386,8 @@ public final class DeviceAdminInfo implements Parcelable { mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED; } else if (deviceOwnerModeStringValue.equalsIgnoreCase("affiliated")) { mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_AFFILIATED; + } else if (deviceOwnerModeStringValue.equalsIgnoreCase("single_user")) { + mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER; } else { throw new XmlPullParserException("headless-system-user mode must be valid"); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c8762c69eeca..c649e622dfc5 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -84,6 +84,7 @@ import android.app.Activity; import android.app.IServiceConnection; import android.app.KeyguardManager; import android.app.admin.SecurityLog.SecurityEvent; +import android.app.admin.flags.Flags; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; @@ -2862,6 +2863,19 @@ public class DevicePolicyManager { @SystemApi public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16; + /** + * Result code for {@link #checkProvisioningPrecondition}. + * + * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when provisioning a DPC into the + * {@link DeviceAdminInfo#HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER} mode but only the system + * user exists on the device. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED) + public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17; + /** * Result codes for {@link #checkProvisioningPrecondition} indicating all the provisioning pre * conditions. @@ -2876,7 +2890,7 @@ public class DevicePolicyManager { STATUS_CANNOT_ADD_MANAGED_PROFILE, STATUS_DEVICE_ADMIN_NOT_SUPPORTED, STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER, STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS, - STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED + STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED, STATUS_HEADLESS_ONLY_SYSTEM_USER }) public @interface ProvisioningPrecondition {} @@ -9178,9 +9192,11 @@ public class DevicePolicyManager { * <p>Calling this after the setup phase of the device owner user has completed is allowed only * if the caller is the {@link Process#SHELL_UID Shell UID}, and there are no additional users * (except when the device runs on headless system user mode, in which case it could have exact - * one extra user, which is the current user - the device owner will be set in the - * {@link UserHandle#SYSTEM system} user and a profile owner will be set in the current user) - * and no accounts. + * one extra user, which is the current user. + * + * <p>On a headless devices, if it is in affiliated mode the device owner will be set in the + * {@link UserHandle#SYSTEM system} user. If the device is in single user mode, the device owner + * will be set in the first secondary user. * * @param who the component name to be registered as device owner. * @param userId ID of the user on which the device owner runs. @@ -11371,7 +11387,9 @@ public class DevicePolicyManager { * @see UserHandle * @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 SecurityException if headless device is in + * {@link DeviceAdminInfo#HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER} mode. + * @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}. @@ -16612,7 +16630,10 @@ public class DevicePolicyManager { * before calling this method. * * <p>Holders of {@link android.Manifest.permission#PROVISION_DEMO_DEVICE} can call this API - * only if {@link FullyManagedDeviceProvisioningParams#isDemoDevice()} is {@code true}.</p> + * only if {@link FullyManagedDeviceProvisioningParams#isDemoDevice()} is {@code true}. + * + * <p>If headless device is in {@link DeviceAdminInfo#HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER} + * mode then it sets the device owner on the first secondary user.</p> * * @param provisioningParams Params required to provision a fully managed device, * see {@link FullyManagedDeviceProvisioningParams}. diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index 6cc8af8c4b51..3c98ef9cfb00 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -76,3 +76,10 @@ flag { description: "Enable APIs to provision and manage eSIMs" bug: "295301164" } + +flag { + name: "headless_device_owner_single_user_enabled" + namespace: "enterprise" + description: "Add Headless DO support." + bug: "289515470" +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 05d1c49b3508..85eac29599f5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -98,6 +98,7 @@ import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION; import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS; import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION; import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED; +import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER; import static android.app.admin.DeviceAdminInfo.USES_POLICY_FORCE_LOCK; import static android.app.admin.DeviceAdminInfo.USES_POLICY_WIPE_DATA; import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED; @@ -189,6 +190,7 @@ import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_SYSTEM_USER_ import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED; import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS; import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER; +import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_ONLY_SYSTEM_USER; import static android.app.admin.DevicePolicyManager.STATUS_OK; import static android.app.admin.DevicePolicyManager.STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS; import static android.app.admin.DevicePolicyManager.STATUS_SYSTEM_USER; @@ -225,6 +227,7 @@ import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAI import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED; import static android.app.admin.flags.Flags.backupServiceSecurityLogEventEnabled; import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled; +import static android.app.admin.flags.Flags.headlessDeviceOwnerSingleUserEnabled; import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled; import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE; import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; @@ -9460,7 +9463,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (setProfileOwnerOnCurrentUserIfNecessary - && mInjector.userManagerIsHeadlessSystemUserMode()) { + && mInjector.userManagerIsHeadlessSystemUserMode() + && getHeadlessDeviceOwnerMode() == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED) { int currentForegroundUser; synchronized (getLockObject()) { currentForegroundUser = getCurrentForegroundUserId(); @@ -9476,6 +9480,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return true; } + private int getHeadlessDeviceOwnerMode() { + synchronized (getLockObject()) { + return getDeviceOwnerAdminLocked().info.getHeadlessDeviceOwnerMode(); + } + } + /** * This API is cached: invalidate with invalidateBinderCaches(). */ @@ -12226,6 +12236,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + admin + " are not in the same package"); } final CallerIdentity caller = getCallerIdentity(admin); + + if (headlessDeviceOwnerSingleUserEnabled()) { + // Block this method if the device is in headless main user mode + Preconditions.checkCallAuthorization( + getHeadlessDeviceOwnerMode() != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER, + "createAndManageUser was called while in headless single user mode"); + } // Only allow the system user to use this method Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), "createAndManageUser was called from non-system user"); @@ -16636,29 +16653,51 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return STATUS_USER_NOT_RUNNING; } + DeviceAdminInfo adminInfo = null; + + boolean isHeadlessModeAffiliated = false; + + boolean isHeadlessModeSingleUser = false; + boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode(); + int ensureSetUpUser = UserHandle.USER_SYSTEM; if (isHeadlessSystemUserMode) { - if (deviceOwnerUserId != UserHandle.USER_SYSTEM) { - Slogf.e(LOG_TAG, "In headless system user mode, " - + "device owner can only be set on headless system user."); - return STATUS_NOT_SYSTEM_USER; - } - if (owner != null) { - DeviceAdminInfo adminInfo = findAdmin( - owner, deviceOwnerUserId, /* throwForMissingPermission= */ false); + adminInfo = findAdmin(owner, + deviceOwnerUserId, /* throwForMissingPermission= */ false); - if (adminInfo.getHeadlessDeviceOwnerMode() - != HEADLESS_DEVICE_OWNER_MODE_AFFILIATED) { + isHeadlessModeAffiliated = + adminInfo.getHeadlessDeviceOwnerMode() + == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED; + + isHeadlessModeSingleUser = + adminInfo.getHeadlessDeviceOwnerMode() + == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER; + + if (!isHeadlessModeAffiliated && !isHeadlessModeSingleUser) { return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED; } + + if (headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) { + ensureSetUpUser = mUserManagerInternal.getMainUserId(); + if (ensureSetUpUser == UserHandle.USER_NULL) { + return STATUS_HEADLESS_ONLY_SYSTEM_USER; + } + } + } + + if (isHeadlessModeAffiliated && deviceOwnerUserId != UserHandle.USER_SYSTEM) { + Slogf.e(LOG_TAG, "In headless system user mode, " + + "device owner can only be set on headless system user."); + return STATUS_NOT_SYSTEM_USER; } + } if (isAdb) { // If shell command runs after user setup completed check device status. Otherwise, OK. - if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { + if (hasUserSetupCompleted(ensureSetUpUser)) { // DO can be setup only if there are no users which are neither created by default // nor marked as FOR_TESTING @@ -16681,11 +16720,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return STATUS_OK; } else { // DO has to be user 0 - if (deviceOwnerUserId != UserHandle.USER_SYSTEM) { + if ((!isHeadlessSystemUserMode || isHeadlessModeAffiliated) + && deviceOwnerUserId != UserHandle.USER_SYSTEM) { return STATUS_NOT_SYSTEM_USER; } // Only provision DO before setup wizard completes - if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { + if (hasUserSetupCompleted(ensureSetUpUser)) { return STATUS_USER_SETUP_COMPLETED; } return STATUS_OK; @@ -21260,7 +21300,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime()); setLocale(provisioningParams.getLocale()); - int deviceOwnerUserId = UserHandle.USER_SYSTEM; + int deviceOwnerUserId = headlessDeviceOwnerSingleUserEnabled() + && getHeadlessDeviceOwnerMode() == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER + ? mUserManagerInternal.getMainUserId() + : UserHandle.USER_SYSTEM; + if (!removeNonRequiredAppsForManagedDevice( deviceOwnerUserId, provisioningParams.isLeaveAllSystemAppsEnabled(), -- GitLab