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

Commit 859372e2 authored by Sergiy Belozorov's avatar Sergiy Belozorov Committed by Android (Google) Code Review
Browse files

Merge "Implement an early prototype for multi-user management device enrollment" into main

parents 1358a72b 1f5c821e
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -1410,6 +1410,7 @@ package android.app.admin {
    method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, android.Manifest.permission.PROVISION_DEMO_DEVICE}) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
    method @FlaggedApi("android.app.admin.flags.multi_user_management_device_provisioning") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionMultiUserDevice(@NonNull android.app.admin.MultiUserDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
    method @FlaggedApi("android.app.admin.flags.remove_managed_profile_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean removeManagedProfile();
    method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
    method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
@@ -1432,6 +1433,7 @@ package android.app.admin {
    field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
    field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
    field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
    field @FlaggedApi("android.app.admin.flags.multi_user_management_device_provisioning") public static final String ACTION_PROVISION_MULTI_USER_DEVICE = "android.app.admin.action.PROVISION_MULTI_USER_DEVICE";
    field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
    field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
    field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
@@ -1497,6 +1499,7 @@ package android.app.admin {
    field public static final int STATUS_HAS_PAIRED = 8; // 0x8
    field 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 @FlaggedApi("android.app.admin.flags.multi_user_management_device_provisioning") public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_REQUIRED = 19; // 0x13
    field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
    field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5
    field public static final int STATUS_NOT_SYSTEM_USER = 7; // 0x7
@@ -1632,6 +1635,23 @@ package android.app.admin {
    method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
  }
  @FlaggedApi("android.app.admin.flags.multi_user_management_device_provisioning") public final class MultiUserDeviceProvisioningParams {
    method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
    method public long getLocalTime();
    method @Nullable public java.util.Locale getLocale();
    method @Nullable public String getTimeZone();
    method public boolean isLeaveAllSystemAppsEnabled();
  }
  @FlaggedApi("android.app.admin.flags.multi_user_management_device_provisioning") public static final class MultiUserDeviceProvisioningParams.Builder {
    ctor public MultiUserDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName);
    method @NonNull public android.app.admin.MultiUserDeviceProvisioningParams build();
    method @NonNull public android.app.admin.MultiUserDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
    method @NonNull public android.app.admin.MultiUserDeviceProvisioningParams.Builder setLocalTime(long);
    method @NonNull public android.app.admin.MultiUserDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
    method @NonNull public android.app.admin.MultiUserDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
  }
  public final class NoArgsPolicyKey extends android.app.admin.PolicyKey {
    method public int describeContents();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
+1 −0
Original line number Diff line number Diff line
@@ -599,6 +599,7 @@ package android.app.admin {
  @RestrictedForEnvironment(environments=android.annotation.RestrictedForEnvironment.ENVIRONMENT_SDK_RUNTIME, from=android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class DevicePolicyManager {
    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer();
    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void calculateHasIncompatibleAccounts();
    method @FlaggedApi("android.app.admin.flags.multi_user_management_device_provisioning") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearMultiUserDeviceManagement(@NonNull android.content.ComponentName);
    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
    method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
    method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
+106 −3
Original line number Diff line number Diff line
@@ -56,12 +56,13 @@ import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.annotation.RestrictedForEnvironment.ENVIRONMENT_SDK_RUNTIME;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
import static android.app.admin.flags.Flags.FLAG_CROSS_PROFILE_WIDGET_PROVIDER_BULK_APIS;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
import static android.app.admin.flags.Flags.FLAG_MULTI_USER_MANAGEMENT_DEVICE_PROVISIONING;
import static android.app.admin.flags.Flags.FLAG_POLICY_STREAMLINING;
import static android.app.admin.flags.Flags.FLAG_REMOVE_MANAGED_PROFILE_ENABLED;
import static android.app.admin.flags.Flags.FLAG_CROSS_PROFILE_WIDGET_PROVIDER_BULK_APIS;
import static android.app.admin.flags.Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED;
import static android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED;
import static android.app.admin.flags.Flags.FLAG_POLICY_STREAMLINING;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
@@ -561,6 +562,30 @@ public class DevicePolicyManager {
    public static final String ACTION_PROVISION_MANAGED_DEVICE
        = "android.app.action.PROVISION_MANAGED_DEVICE";
    // TODO(b/390162247): Rename this since it's no longer an intent action. It
    // can be moved out of the ACTION_PROVISION_* constant group and potentially
    // migrated to an enum with other modes. Also, consider renaming the
    // checkProvisioningPrecondition parameter to managementMode.
    /**
     * Constant to indicate multi-user device provisioning.
     *
     * <p> When multi-user device provisioning has completed, an intent of the type
     * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is
     * broadcast. The extra {@link #EXTRA_PROVISIONING_ACTION} will be set to
     * {@link #ACTION_PROVISION_MULTI_USER_DEVICE}.
     *
     * <p> This can also be passed to {@link #checkProvisioningPrecondition} to check if the
     * multi-user device provisioning is allowed.
     *
     * @hide
     */
    @SystemApi
    @FlaggedApi(Flags.FLAG_MULTI_USER_MANAGEMENT_DEVICE_PROVISIONING)
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    public static final String ACTION_PROVISION_MULTI_USER_DEVICE =
            "android.app.admin.action.PROVISION_MULTI_USER_DEVICE";
    /**
     * Activity action: launch when user provisioning completed, i.e.
     * {@link #getUserProvisioningState()} returns one of the complete state.
@@ -2984,6 +3009,18 @@ public class DevicePolicyManager {
    public static final int STATUS_HEADLESS_SINGLE_USER_MODE_ONLY_SUPPORTED_ON_FIRST_FULL_USER = 18;
    /**
     * Result code for {@link #checkProvisioningPreCondition}.
     *
     * <p>Returned for {@link #ACTION_PROVISION_MULTI_USER_DEVICE} when the device is not
     * running in headless system user mode.
     *
     * @hide
     */
    @SystemApi
    @FlaggedApi(Flags.FLAG_MULTI_USER_MANAGEMENT_DEVICE_PROVISIONING)
    public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_REQUIRED = 19;
    /**
     * Result codes for {@link #checkProvisioningPrecondition} indicating all the provisioning pre
     * conditions.
@@ -2999,7 +3036,8 @@ public class DevicePolicyManager {
            STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
            STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS,
            STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED, STATUS_HEADLESS_ONLY_SYSTEM_USER,
            STATUS_HEADLESS_SINGLE_USER_MODE_ONLY_SUPPORTED_ON_FIRST_FULL_USER
            STATUS_HEADLESS_SINGLE_USER_MODE_ONLY_SUPPORTED_ON_FIRST_FULL_USER,
            STATUS_HEADLESS_SYSTEM_USER_MODE_REQUIRED
    })
    public @interface ProvisioningPrecondition {}
@@ -9986,6 +10024,16 @@ public class DevicePolicyManager {
    @SystemApi
    @SuppressLint("RequiresPermission")
    public boolean isDeviceManaged() {
        // TODO(b/390162247): Add API level check to avoid breaking existing apps targeting old API.
        if (android.app.admin.flags.Flags.multiUserManagementDeviceProvisioning()
                && mService != null) {
            try {
                // TODO(b/390162247): Consider adding a cache just like we do for hasDeviceOwner.
                return mService.isDeviceManaged();
            } catch (RemoteException re) {
                throw re.rethrowFromSystemServer();
            }
        }
        return mHasDeviceOwnerCache.query(null);
    }
@@ -15552,6 +15600,30 @@ public class DevicePolicyManager {
        }
    }
    /**
     * Clears the multi-user device management state for testing purposes. Can only remove
     * management set up by test packages. Does not send a broadcast about the removal.
     *
     * @param adminReceiver The administration compononent to remove.
     * @throws SecurityException if the caller is not shell / root or the admin package
     *         isn't a test application see {@link ApplicationInfo#FLAG_TEST_APP}.
     * @hide
     */
    @TestApi
    @FlaggedApi(Flags.FLAG_MULTI_USER_MANAGEMENT_DEVICE_PROVISIONING)
    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
    // TODO(b/390162247): Remove adminReceiver param once we decide where to store
    // provisioning-related data instead of ActiveAdmin.
    public void clearMultiUserDeviceManagement(@NonNull ComponentName adminReceiver) {
        try {
            if (mService != null) {
                mService.clearMultiUserDeviceManagement(adminReceiver);
            }
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }
    /**
     * Returns whether the device has been provisioned.
     *
@@ -17734,6 +17806,37 @@ public class DevicePolicyManager {
        }
    }
    /**
     * Provisions a device intended for use by multiple users for management.
     *
     * <p>The method {@link #checkProvisioningPrecondition} must be returning {@link #STATUS_OK}
     * before calling this method. If it doesn't, a {@link ProvisioningException} will be thrown.
     *
     * @param provisioningParams Params required to provision a multi-user device, see
     * {@link MultiUserDeviceProvisioningParams}.
     *
     * @throws ProvisioningException if an error occurred during provisioning.
     *
     * @hide
     */
    @SystemApi
    @FlaggedApi(FLAG_MULTI_USER_MANAGEMENT_DEVICE_PROVISIONING)
    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
    public void provisionMultiUserDevice(
            @NonNull MultiUserDeviceProvisioningParams provisioningParams)
            throws ProvisioningException {
        if (mService != null) {
            try {
                mService.provisionMultiUserDevice(
                        provisioningParams.getTransportParams(), mContext.getPackageName());
            } catch (ServiceSpecificException e) {
                throw new ProvisioningException(e, e.errorCode, getErrorMessage(e));
            } catch (RemoteException re) {
                throw re.rethrowFromSystemServer();
            }
        }
    }
    /**
     * Resets the default cross profile intent filters that were set during
     * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+6 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.app.admin.IAuditLogEventsCallback;
import android.app.admin.ManagedProfileProvisioningParams;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.MultiUserDeviceProvisioningParamsTransport;
import android.app.admin.WifiSsidPolicy;
import android.content.ComponentName;
import android.content.Intent;
@@ -171,6 +172,8 @@ interface IDevicePolicyManager {
    void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result, int userHandle);
    void removeActiveAdmin(in ComponentName policyReceiver, int userHandle);
    void forceRemoveActiveAdmin(in ComponentName policyReceiver, int userHandle);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)")
    void clearMultiUserDeviceManagement(in ComponentName admin);
    boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);

    void reportPasswordChanged(in PasswordMetrics metrics, int userId);
@@ -188,6 +191,7 @@ interface IDevicePolicyManager {
    String getDeviceOwnerName();
    void clearDeviceOwner(String packageName);
    int getDeviceOwnerUserId();
    boolean isDeviceManaged();

    boolean setProfileOwner(in ComponentName who, int userHandle);
    ComponentName getProfileOwnerAsUser(int userHandle);
@@ -580,6 +584,8 @@ interface IDevicePolicyManager {
    UserHandle createManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
    void finalizeCreateManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in UserHandle managedProfileUser);
    void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)")
    void provisionMultiUserDevice(in MultiUserDeviceProvisioningParamsTransport provisioningParamsTransport, in String callerPackage);

    void finalizeWorkProfileProvisioning(in UserHandle managedProfileUser, in Account migratedAccount);

+210 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.app.admin;

import static java.util.Objects.requireNonNull;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.stats.devicepolicy.DevicePolicyEnums;

import java.util.Locale;

/**
 * Params required to provision a multi-user managed device, see {@link
 * DevicePolicyManager#provisionMultiUserDevice}.
 *
 * @hide
 */
@SystemApi
@FlaggedApi(Flags.FLAG_MULTI_USER_MANAGEMENT_DEVICE_PROVISIONING)
public final class MultiUserDeviceProvisioningParams {
    private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
            "LEAVE_ALL_SYSTEM_APPS_ENABLED";
    private static final String TIME_ZONE_PROVIDED_PARAM = "TIME_ZONE_PROVIDED";
    private static final String LOCALE_PROVIDED_PARAM = "LOCALE_PROVIDED";

    private final MultiUserDeviceProvisioningParamsTransport mTransportParams;

    /**
     * Reconstruct params from the transport representation after receiving in the service.
     *
     * @hide
     */
    public MultiUserDeviceProvisioningParams(
            MultiUserDeviceProvisioningParamsTransport transportParams) {
        this.mTransportParams = transportParams;
    }

    /**
     * Generates a transport representation to be passed from DevicePolicyManager to its service.
     */
    MultiUserDeviceProvisioningParamsTransport getTransportParams() {
        return mTransportParams;
    }

    @Nullable
    private static Locale getLocale(@Nullable String localeStr) {
        return localeStr == null ? null : Locale.forLanguageTag(localeStr);
    }

    /** Returns the device controller's {@link ComponentName}. */
    @NonNull
    public ComponentName getDeviceAdminComponentName() {
        return mTransportParams.deviceAdminComponentName;
    }

    /** Returns {@code true} if system apps should be left enabled after provisioning. */
    public boolean isLeaveAllSystemAppsEnabled() {
        return mTransportParams.leaveAllSystemAppsEnabled;
    }

    /**
     * If set, it returns the time zone to set for the device after provisioning, otherwise returns
     * {@code null};
     */
    @Nullable
    public String getTimeZone() {
        return mTransportParams.timeZone;
    }

    /**
     * If set, it returns the local time to set for the device after provisioning, otherwise returns
     * 0.
     */
    public long getLocalTime() {
        return mTransportParams.localTime;
    }

    /**
     * If set, it returns the {@link Locale} to set for the device after provisioning, otherwise
     * returns {@code null}.
     */
    @Nullable
    public @SuppressLint("UseIcu") Locale getLocale() {
        return getLocale(mTransportParams.localeStr);
    }

    /**
     * Logs the provisioning params using {@link DevicePolicyEventLogger}.
     *
     * @hide
     */
    public void logParams(@NonNull String callerPackage) {
        requireNonNull(callerPackage);

        logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM,
                 mTransportParams.leaveAllSystemAppsEnabled);
        logParam(callerPackage, TIME_ZONE_PROVIDED_PARAM,
                 /* value= */ mTransportParams.timeZone != null);
        logParam(callerPackage, LOCALE_PROVIDED_PARAM,
                  /* value= */ mTransportParams.localeStr != null);
    }

    private void logParam(String callerPackage, String param, boolean value) {
        DevicePolicyEventLogger.createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_PARAM)
                .setStrings(callerPackage)
                .setAdmin(mTransportParams.deviceAdminComponentName)
                .setStrings(param)
                .setBoolean(value)
                .write();
    }

    /**
     * Builder class for {@link MultiUserDeviceProvisioningParams} objects.
     *
     * @hide
     */
    @SystemApi
    @FlaggedApi(Flags.FLAG_MULTI_USER_MANAGEMENT_DEVICE_PROVISIONING)
    public static final class Builder {
        private final MultiUserDeviceProvisioningParamsTransport mTransportParams;

        /**
         * Initialize a new {@link Builder} to construct a {@link
         * MultiUserDeviceProvisioningParams}.
         *
         * <p>See {@link DevicePolicyManager#provisionMultiUserDevice}
         *
         * @param deviceAdminComponentName The admin {@link ComponentName} to be set as the device
         *     controller.
         * @throws NullPointerException if {@code deviceAdminComponentName} are null.
         */
        public Builder(@NonNull ComponentName deviceAdminComponentName) {
            requireNonNull(deviceAdminComponentName);
            this.mTransportParams = new MultiUserDeviceProvisioningParamsTransport();
            this.mTransportParams.deviceAdminComponentName = deviceAdminComponentName;

        }

        /**
         * Sets whether non-required system apps should be installed on the created profile when
         * {@link DevicePolicyManager#provisionMultiUserDevice} is called. Defaults to {@code
         * false} if not set.
         */
        @NonNull
        public Builder setLeaveAllSystemAppsEnabled(boolean leaveAllSystemAppsEnabled) {
            this.mTransportParams.leaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
            return this;
        }

        /**
         * Sets {@code timeZone} on the device. If not set or set to {@code null}, {@link
         * DevicePolicyManager#provisionMultiUserDevice} will not set a timezone
         */
        @NonNull
        public Builder setTimeZone(@Nullable String timeZone) {
            this.mTransportParams.timeZone = timeZone;
            return this;
        }

        /**
         * Sets {@code localTime} on the device, If not set or set to {@code 0}, {@link
         * DevicePolicyManager#provisionMultiUserDevice} will not set a local time.
         */
        @NonNull
        public Builder setLocalTime(long localTime) {
            this.mTransportParams.localTime = localTime;
            return this;
        }

        /**
         * Sets {@link Locale} on the device, If not set or set to {@code null}, {@link
         * DevicePolicyManager#provisionMultiUserDevice} will not set a locale.
         */
        @NonNull
        public Builder setLocale(@SuppressLint("UseIcu") @Nullable Locale locale) {
            this.mTransportParams.localeStr = (locale != null) ? locale.toLanguageTag() : null;
            return this;
        }

        /**
         * Combines all of the attributes that have been set on this {@code Builder}
         *
         * @return a new {@link MultiUserDeviceProvisioningParams} object.
         */
        @NonNull
        public MultiUserDeviceProvisioningParams build() {
            return new MultiUserDeviceProvisioningParams(mTransportParams);
        }
    }
}
Loading