Loading core/api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -7669,6 +7669,7 @@ package android.app.admin { method public boolean updateOverrideApn(@NonNull android.content.ComponentName, int, @NonNull android.telephony.data.ApnSetting); method public void wipeData(int); method public void wipeData(int, @NonNull CharSequence); method public void wipeDevice(int); field public static final String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE"; field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; core/java/android/app/admin/DevicePolicyManager.java +70 −38 Original line number Diff line number Diff line Loading @@ -171,6 +171,7 @@ public class DevicePolicyManager { private final boolean mParentInstance; private final DevicePolicyResourcesManager mResourcesManager; /** @hide */ public DevicePolicyManager(Context context, IDevicePolicyManager service) { this(context, service, false); Loading Loading @@ -6207,46 +6208,46 @@ public class DevicePolicyManager { public static final int WIPE_SILENTLY = 0x0008; /** * Ask that all user data be wiped. If called as a secondary user, the user will be removed and * other users will remain unaffected. Calling from the primary user will cause the device to * reboot, erasing all device data - including all the secondary users and their data - while * booting up. * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * be able to call this method; if it has not, a security exception will be thrown. * * If the caller is a profile owner of an organization-owned managed profile, it may * additionally call this method on the parent instance. * Calling this method on the parent {@link DevicePolicyManager} instance would wipe the * entire device, while calling it on the current profile instance would relinquish the device * for personal use, removing the managed profile and all policies set by the profile owner. * See {@link #wipeData(int, CharSequence)} * * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} or is not granted the * @throws SecurityException if the calling application does not own an active * administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} and is * not granted the * {@link android.Manifest.permission#MASTER_CLEAR} permission. * @throws IllegalStateException if called on last full-user or system-user * @see #wipeDevice(int) * @see #wipeData(int, CharSequence) */ public void wipeData(int flags) { wipeDataInternal(flags, ""); wipeDataInternal(flags, /* wipeReasonForUser= */ "", /* factoryReset= */ false); } /** * Ask that all user data be wiped. If called as a secondary user, the user will be removed and * other users will remain unaffected, the provided reason for wiping data can be shown to * user. Calling from the primary user will cause the device to reboot, erasing all device data * - including all the secondary users and their data - while booting up. In this case, we don't * show the reason to the user since the device would be factory reset. * Ask that all user data be wiped. * * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * be able to call this method; if it has not, a security exception will be thrown. * If called as a secondary user or managed profile, the user itself and its associated user * data will be wiped. In particular, If the caller is a profile owner of an * organization-owned managed profile, calling this method will relinquish the device for * personal use, removing the managed profile and all policies set by the profile owner. * </p> * * If the caller is a profile owner of an organization-owned managed profile, it may * additionally call this method on the parent instance. * Calling this method on the parent {@link DevicePolicyManager} instance would wipe the * entire device, while calling it on the current profile instance would relinquish the device * for personal use, removing the managed profile and all policies set by the profile owner. * <p> * Calling this method from the primary user will only work if the calling app is targeting * Android 13 or below, in which case it will cause the device to reboot, erasing all device * data - including all the secondary users and their data - while booting up. If an app * targeting Android 13+ is calling this method from the primary user or last full user, * {@link IllegalStateException} will be thrown. * </p> * * If an app wants to wipe the entire device irrespective of which user they are from, they * should use {@link #wipeDevice} instead. * * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and Loading @@ -6254,30 +6255,61 @@ public class DevicePolicyManager { * @param reason a string that contains the reason for wiping data, which can be * presented to the user. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} or is not granted the * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} and is not granted the * {@link android.Manifest.permission#MASTER_CLEAR} permission. * @throws IllegalArgumentException if the input reason string is null or empty, or if * {@link #WIPE_SILENTLY} is set. * @throws IllegalStateException if called on last full-user or system-user * @see #wipeDevice(int) * @see #wipeData(int) */ public void wipeData(int flags, @NonNull CharSequence reason) { Objects.requireNonNull(reason, "reason string is null"); Preconditions.checkStringNotEmpty(reason, "reason string is empty"); Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set"); wipeDataInternal(flags, reason.toString()); wipeDataInternal(flags, reason.toString(), /* factoryReset= */ false); } /** * Internal function for both {@link #wipeData(int)} and * {@link #wipeData(int, CharSequence)} to call. * Ask that the device be wiped and factory reset. * * <p> * The calling Device Owner or Organization Owned Profile Owner must have requested * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to be able to call this method; if it has * not, a security exception will be thrown. * * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} and is not * granted the {@link android.Manifest.permission#MASTER_CLEAR} * permission. * @see #wipeData(int) * @see #wipeData(int, CharSequence) */ // TODO(b/255323293) Add host-side tests public void wipeDevice(int flags) { wipeDataInternal(flags, /* wipeReasonForUser= */ "", /* factoryReset= */ true); } /** * Internal function for {@link #wipeData(int)}, {@link #wipeData(int, CharSequence)} * and {@link #wipeDevice(int)} to call. * * @hide * @see #wipeData(int) * @see #wipeData(int, CharSequence) * @see #wipeDevice(int) */ private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser, boolean factoryReset) { if (mService != null) { try { mService.wipeDataWithReason(flags, wipeReasonForUser, mParentInstance); mService.wipeDataWithReason(flags, wipeReasonForUser, mParentInstance, factoryReset); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -8642,7 +8674,7 @@ public class DevicePolicyManager { public void reportFailedPasswordAttempt(int userHandle) { if (mService != null) { try { mService.reportFailedPasswordAttempt(userHandle); mService.reportFailedPasswordAttempt(userHandle, mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading core/java/android/app/admin/IDevicePolicyManager.aidl +5 −2 Original line number Diff line number Diff line Loading @@ -117,7 +117,10 @@ interface IDevicePolicyManager { void lockNow(int flags, boolean parent); void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent); /** * @param factoryReset only applicable when `targetSdk >= U`, either tries to factoryReset/fail or removeUser/fail otherwise **/ void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent, boolean factoryReset); void setFactoryResetProtectionPolicy(in ComponentName who, in FactoryResetProtectionPolicy policy); FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(in ComponentName who); Loading Loading @@ -161,7 +164,7 @@ interface IDevicePolicyManager { boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle); void reportPasswordChanged(in PasswordMetrics metrics, int userId); void reportFailedPasswordAttempt(int userHandle); void reportFailedPasswordAttempt(int userHandle, boolean parent); void reportSuccessfulPasswordAttempt(int userHandle); void reportFailedBiometricAttempt(int userHandle); void reportSuccessfulBiometricAttempt(int userHandle); Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +59 −11 Original line number Diff line number Diff line Loading @@ -646,6 +646,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long USE_SET_LOCATION_ENABLED = 117835097L; /** * Forces wipeDataNoLock to attempt removing the user or throw an error as * opposed to trying to factory reset the device first and only then falling back to user * removal. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long EXPLICIT_WIPE_BEHAVIOUR = 242193913L; // Only add to the end of the list. Do not change or rearrange these values, that will break // historical data. Do not use negative numbers or zero, logger only handles positive // integers. Loading Loading @@ -6699,8 +6708,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override public void wipeDataWithReason(int flags, String wipeReasonForUser, boolean calledOnParentInstance) { public void wipeDataWithReason(int flags, @NonNull String wipeReasonForUser, boolean calledOnParentInstance, boolean factoryReset) { if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) { return; } Loading Loading @@ -6782,7 +6791,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "DevicePolicyManager.wipeDataWithReason() from %s, organization-owned? %s", adminName, calledByProfileOwnerOnOrgOwnedDevice); wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId); wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId, calledOnParentInstance, factoryReset); } private String getGenericWipeReason( Loading Loading @@ -6844,8 +6854,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slogf.i(LOG_TAG, "Cleaning up device-wide policies done."); } /** * @param factoryReset null: legacy behaviour, false: attempt to remove user, true: attempt to * factory reset */ private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, String wipeReasonForUser, int userId) { @NonNull String wipeReasonForUser, int userId, boolean calledOnParentInstance, @Nullable Boolean factoryReset) { wtfIfInLock(); mInjector.binderWithCleanCallingIdentity(() -> { Loading @@ -6863,7 +6878,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " restriction is set for user " + userId); } if (userId == UserHandle.USER_SYSTEM) { boolean isSystemUser = userId == UserHandle.USER_SYSTEM; boolean wipeDevice; if (factoryReset == null || !CompatChanges.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR)) { // Legacy mode wipeDevice = isSystemUser; } else { // Explicit behaviour if (factoryReset) { // TODO(b/254031494) Replace with new factory reset permission checks boolean hasPermission = isDeviceOwnerUserId(userId) || (isOrganizationOwnedDeviceWithManagedProfile() && calledOnParentInstance); Preconditions.checkState(hasPermission, "Admin %s does not have permission to factory reset the device.", userId); wipeDevice = true; } else { Preconditions.checkCallAuthorization(!isSystemUser, "User %s is a system user and cannot be removed", userId); boolean isLastNonHeadlessUser = getUserInfo(userId).isFull() && mUserManager.getAliveUsers().stream() .filter((it) -> it.getUserHandle().getIdentifier() != userId) .noneMatch(UserInfo::isFull); Preconditions.checkState(!isLastNonHeadlessUser, "Removing user %s would leave the device without any active users. " + "Consider factory resetting the device instead.", userId); wipeDevice = false; } } if (wipeDevice) { forceWipeDeviceNoLock( (flags & WIPE_EXTERNAL_STORAGE) != 0, internalReason, Loading Loading @@ -7131,7 +7176,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override public void reportFailedPasswordAttempt(int userHandle) { public void reportFailedPasswordAttempt(int userHandle, boolean parent) { Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); final CallerIdentity caller = getCallerIdentity(); Loading @@ -7153,7 +7198,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { saveSettingsLocked(userHandle); if (mHasFeature) { strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked( userHandle, /* parent */ false); userHandle, /* parent= */ false); int max = strictestAdmin != null ? strictestAdmin.maximumFailedPasswordsForWipe : 0; if (max > 0 && policy.mFailedPasswordAttempts >= max) { Loading Loading @@ -7186,7 +7231,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /* flags= */ 0, /* reason= */ "reportFailedPasswordAttempt()", getFailedPasswordAttemptWipeMessage(), userId); userId, /* calledOnParentInstance= */ parent, // factoryReset=null to enable U- behaviour /* factoryReset= */ null); } catch (SecurityException e) { Slogf.w(LOG_TAG, "Failed to wipe user " + userId + " after max failed password attempts reached.", e); Loading @@ -7195,7 +7243,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, /*method strength*/ 1); /* result= */ 0, /* method strength= */ 1); } } Loading services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +8 −8 Original line number Diff line number Diff line Loading @@ -140,7 +140,7 @@ import android.security.KeyChain; import android.security.keystore.AttestationUtils; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth import android.test.MoreAsserts; import android.util.ArraySet; import android.util.Log; import android.util.Pair; Loading Loading @@ -5087,7 +5087,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test public void testWipeDataDeviceOwner() throws Exception { public void testWipeDevice_DeviceOwner() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, Loading @@ -5096,7 +5096,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). thenReturn("Just a test string."); dpm.wipeData(0); dpm.wipeDevice(0); verifyRebootWipeUserData(/* wipeEuicc= */ false); } Loading @@ -5111,13 +5111,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). thenReturn("Just a test string."); dpm.wipeData(WIPE_EUICC); dpm.wipeDevice(WIPE_EUICC); verifyRebootWipeUserData(/* wipeEuicc= */ true); } @Test public void testWipeDataDeviceOwnerDisallowed() throws Exception { public void testWipeDevice_DeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, Loading @@ -5128,7 +5128,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // The DO is not allowed to wipe the device if the user restriction was set // by the system assertExpectException(SecurityException.class, /* messageRegex= */ null, () -> dpm.wipeData(0)); () -> dpm.wipeDevice(0)); } @Test Loading Loading @@ -7986,7 +7986,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test public void testWipeData_financeDo_success() throws Exception { public void testWipeDevice_financeDo_success() throws Exception { setDeviceOwner(); dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); when(getServices().userManager.getUserRestrictionSource( Loading @@ -7997,7 +7997,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .getString(R.string.work_profile_deleted_description_dpm_wipe)) .thenReturn("Test string"); dpm.wipeData(0); dpm.wipeDevice(0); verifyRebootWipeUserData(/* wipeEuicc= */ false); } Loading Loading
core/api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -7669,6 +7669,7 @@ package android.app.admin { method public boolean updateOverrideApn(@NonNull android.content.ComponentName, int, @NonNull android.telephony.data.ApnSetting); method public void wipeData(int); method public void wipeData(int, @NonNull CharSequence); method public void wipeDevice(int); field public static final String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE"; field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
core/java/android/app/admin/DevicePolicyManager.java +70 −38 Original line number Diff line number Diff line Loading @@ -171,6 +171,7 @@ public class DevicePolicyManager { private final boolean mParentInstance; private final DevicePolicyResourcesManager mResourcesManager; /** @hide */ public DevicePolicyManager(Context context, IDevicePolicyManager service) { this(context, service, false); Loading Loading @@ -6207,46 +6208,46 @@ public class DevicePolicyManager { public static final int WIPE_SILENTLY = 0x0008; /** * Ask that all user data be wiped. If called as a secondary user, the user will be removed and * other users will remain unaffected. Calling from the primary user will cause the device to * reboot, erasing all device data - including all the secondary users and their data - while * booting up. * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * be able to call this method; if it has not, a security exception will be thrown. * * If the caller is a profile owner of an organization-owned managed profile, it may * additionally call this method on the parent instance. * Calling this method on the parent {@link DevicePolicyManager} instance would wipe the * entire device, while calling it on the current profile instance would relinquish the device * for personal use, removing the managed profile and all policies set by the profile owner. * See {@link #wipeData(int, CharSequence)} * * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} or is not granted the * @throws SecurityException if the calling application does not own an active * administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} and is * not granted the * {@link android.Manifest.permission#MASTER_CLEAR} permission. * @throws IllegalStateException if called on last full-user or system-user * @see #wipeDevice(int) * @see #wipeData(int, CharSequence) */ public void wipeData(int flags) { wipeDataInternal(flags, ""); wipeDataInternal(flags, /* wipeReasonForUser= */ "", /* factoryReset= */ false); } /** * Ask that all user data be wiped. If called as a secondary user, the user will be removed and * other users will remain unaffected, the provided reason for wiping data can be shown to * user. Calling from the primary user will cause the device to reboot, erasing all device data * - including all the secondary users and their data - while booting up. In this case, we don't * show the reason to the user since the device would be factory reset. * Ask that all user data be wiped. * * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * be able to call this method; if it has not, a security exception will be thrown. * If called as a secondary user or managed profile, the user itself and its associated user * data will be wiped. In particular, If the caller is a profile owner of an * organization-owned managed profile, calling this method will relinquish the device for * personal use, removing the managed profile and all policies set by the profile owner. * </p> * * If the caller is a profile owner of an organization-owned managed profile, it may * additionally call this method on the parent instance. * Calling this method on the parent {@link DevicePolicyManager} instance would wipe the * entire device, while calling it on the current profile instance would relinquish the device * for personal use, removing the managed profile and all policies set by the profile owner. * <p> * Calling this method from the primary user will only work if the calling app is targeting * Android 13 or below, in which case it will cause the device to reboot, erasing all device * data - including all the secondary users and their data - while booting up. If an app * targeting Android 13+ is calling this method from the primary user or last full user, * {@link IllegalStateException} will be thrown. * </p> * * If an app wants to wipe the entire device irrespective of which user they are from, they * should use {@link #wipeDevice} instead. * * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and Loading @@ -6254,30 +6255,61 @@ public class DevicePolicyManager { * @param reason a string that contains the reason for wiping data, which can be * presented to the user. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} or is not granted the * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} and is not granted the * {@link android.Manifest.permission#MASTER_CLEAR} permission. * @throws IllegalArgumentException if the input reason string is null or empty, or if * {@link #WIPE_SILENTLY} is set. * @throws IllegalStateException if called on last full-user or system-user * @see #wipeDevice(int) * @see #wipeData(int) */ public void wipeData(int flags, @NonNull CharSequence reason) { Objects.requireNonNull(reason, "reason string is null"); Preconditions.checkStringNotEmpty(reason, "reason string is empty"); Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set"); wipeDataInternal(flags, reason.toString()); wipeDataInternal(flags, reason.toString(), /* factoryReset= */ false); } /** * Internal function for both {@link #wipeData(int)} and * {@link #wipeData(int, CharSequence)} to call. * Ask that the device be wiped and factory reset. * * <p> * The calling Device Owner or Organization Owned Profile Owner must have requested * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to be able to call this method; if it has * not, a security exception will be thrown. * * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} and is not * granted the {@link android.Manifest.permission#MASTER_CLEAR} * permission. * @see #wipeData(int) * @see #wipeData(int, CharSequence) */ // TODO(b/255323293) Add host-side tests public void wipeDevice(int flags) { wipeDataInternal(flags, /* wipeReasonForUser= */ "", /* factoryReset= */ true); } /** * Internal function for {@link #wipeData(int)}, {@link #wipeData(int, CharSequence)} * and {@link #wipeDevice(int)} to call. * * @hide * @see #wipeData(int) * @see #wipeData(int, CharSequence) * @see #wipeDevice(int) */ private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser, boolean factoryReset) { if (mService != null) { try { mService.wipeDataWithReason(flags, wipeReasonForUser, mParentInstance); mService.wipeDataWithReason(flags, wipeReasonForUser, mParentInstance, factoryReset); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -8642,7 +8674,7 @@ public class DevicePolicyManager { public void reportFailedPasswordAttempt(int userHandle) { if (mService != null) { try { mService.reportFailedPasswordAttempt(userHandle); mService.reportFailedPasswordAttempt(userHandle, mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading
core/java/android/app/admin/IDevicePolicyManager.aidl +5 −2 Original line number Diff line number Diff line Loading @@ -117,7 +117,10 @@ interface IDevicePolicyManager { void lockNow(int flags, boolean parent); void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent); /** * @param factoryReset only applicable when `targetSdk >= U`, either tries to factoryReset/fail or removeUser/fail otherwise **/ void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent, boolean factoryReset); void setFactoryResetProtectionPolicy(in ComponentName who, in FactoryResetProtectionPolicy policy); FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(in ComponentName who); Loading Loading @@ -161,7 +164,7 @@ interface IDevicePolicyManager { boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle); void reportPasswordChanged(in PasswordMetrics metrics, int userId); void reportFailedPasswordAttempt(int userHandle); void reportFailedPasswordAttempt(int userHandle, boolean parent); void reportSuccessfulPasswordAttempt(int userHandle); void reportFailedBiometricAttempt(int userHandle); void reportSuccessfulBiometricAttempt(int userHandle); Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +59 −11 Original line number Diff line number Diff line Loading @@ -646,6 +646,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long USE_SET_LOCATION_ENABLED = 117835097L; /** * Forces wipeDataNoLock to attempt removing the user or throw an error as * opposed to trying to factory reset the device first and only then falling back to user * removal. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long EXPLICIT_WIPE_BEHAVIOUR = 242193913L; // Only add to the end of the list. Do not change or rearrange these values, that will break // historical data. Do not use negative numbers or zero, logger only handles positive // integers. Loading Loading @@ -6699,8 +6708,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override public void wipeDataWithReason(int flags, String wipeReasonForUser, boolean calledOnParentInstance) { public void wipeDataWithReason(int flags, @NonNull String wipeReasonForUser, boolean calledOnParentInstance, boolean factoryReset) { if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) { return; } Loading Loading @@ -6782,7 +6791,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "DevicePolicyManager.wipeDataWithReason() from %s, organization-owned? %s", adminName, calledByProfileOwnerOnOrgOwnedDevice); wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId); wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId, calledOnParentInstance, factoryReset); } private String getGenericWipeReason( Loading Loading @@ -6844,8 +6854,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slogf.i(LOG_TAG, "Cleaning up device-wide policies done."); } /** * @param factoryReset null: legacy behaviour, false: attempt to remove user, true: attempt to * factory reset */ private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, String wipeReasonForUser, int userId) { @NonNull String wipeReasonForUser, int userId, boolean calledOnParentInstance, @Nullable Boolean factoryReset) { wtfIfInLock(); mInjector.binderWithCleanCallingIdentity(() -> { Loading @@ -6863,7 +6878,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " restriction is set for user " + userId); } if (userId == UserHandle.USER_SYSTEM) { boolean isSystemUser = userId == UserHandle.USER_SYSTEM; boolean wipeDevice; if (factoryReset == null || !CompatChanges.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR)) { // Legacy mode wipeDevice = isSystemUser; } else { // Explicit behaviour if (factoryReset) { // TODO(b/254031494) Replace with new factory reset permission checks boolean hasPermission = isDeviceOwnerUserId(userId) || (isOrganizationOwnedDeviceWithManagedProfile() && calledOnParentInstance); Preconditions.checkState(hasPermission, "Admin %s does not have permission to factory reset the device.", userId); wipeDevice = true; } else { Preconditions.checkCallAuthorization(!isSystemUser, "User %s is a system user and cannot be removed", userId); boolean isLastNonHeadlessUser = getUserInfo(userId).isFull() && mUserManager.getAliveUsers().stream() .filter((it) -> it.getUserHandle().getIdentifier() != userId) .noneMatch(UserInfo::isFull); Preconditions.checkState(!isLastNonHeadlessUser, "Removing user %s would leave the device without any active users. " + "Consider factory resetting the device instead.", userId); wipeDevice = false; } } if (wipeDevice) { forceWipeDeviceNoLock( (flags & WIPE_EXTERNAL_STORAGE) != 0, internalReason, Loading Loading @@ -7131,7 +7176,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override public void reportFailedPasswordAttempt(int userHandle) { public void reportFailedPasswordAttempt(int userHandle, boolean parent) { Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); final CallerIdentity caller = getCallerIdentity(); Loading @@ -7153,7 +7198,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { saveSettingsLocked(userHandle); if (mHasFeature) { strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked( userHandle, /* parent */ false); userHandle, /* parent= */ false); int max = strictestAdmin != null ? strictestAdmin.maximumFailedPasswordsForWipe : 0; if (max > 0 && policy.mFailedPasswordAttempts >= max) { Loading Loading @@ -7186,7 +7231,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /* flags= */ 0, /* reason= */ "reportFailedPasswordAttempt()", getFailedPasswordAttemptWipeMessage(), userId); userId, /* calledOnParentInstance= */ parent, // factoryReset=null to enable U- behaviour /* factoryReset= */ null); } catch (SecurityException e) { Slogf.w(LOG_TAG, "Failed to wipe user " + userId + " after max failed password attempts reached.", e); Loading @@ -7195,7 +7243,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, /*method strength*/ 1); /* result= */ 0, /* method strength= */ 1); } } Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +8 −8 Original line number Diff line number Diff line Loading @@ -140,7 +140,7 @@ import android.security.KeyChain; import android.security.keystore.AttestationUtils; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth import android.test.MoreAsserts; import android.util.ArraySet; import android.util.Log; import android.util.Pair; Loading Loading @@ -5087,7 +5087,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test public void testWipeDataDeviceOwner() throws Exception { public void testWipeDevice_DeviceOwner() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, Loading @@ -5096,7 +5096,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). thenReturn("Just a test string."); dpm.wipeData(0); dpm.wipeDevice(0); verifyRebootWipeUserData(/* wipeEuicc= */ false); } Loading @@ -5111,13 +5111,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). thenReturn("Just a test string."); dpm.wipeData(WIPE_EUICC); dpm.wipeDevice(WIPE_EUICC); verifyRebootWipeUserData(/* wipeEuicc= */ true); } @Test public void testWipeDataDeviceOwnerDisallowed() throws Exception { public void testWipeDevice_DeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, Loading @@ -5128,7 +5128,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // The DO is not allowed to wipe the device if the user restriction was set // by the system assertExpectException(SecurityException.class, /* messageRegex= */ null, () -> dpm.wipeData(0)); () -> dpm.wipeDevice(0)); } @Test Loading Loading @@ -7986,7 +7986,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test public void testWipeData_financeDo_success() throws Exception { public void testWipeDevice_financeDo_success() throws Exception { setDeviceOwner(); dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); when(getServices().userManager.getUserRestrictionSource( Loading @@ -7997,7 +7997,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .getString(R.string.work_profile_deleted_description_dpm_wipe)) .thenReturn("Test string"); dpm.wipeData(0); dpm.wipeDevice(0); verifyRebootWipeUserData(/* wipeEuicc= */ false); } Loading