Loading core/java/android/app/admin/DevicePolicyManager.java +20 −7 Original line number Original line Diff line number Diff line Loading @@ -88,7 +88,6 @@ import android.telephony.data.ApnSetting; import android.util.ArraySet; import android.util.ArraySet; import android.util.Log; import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions; Loading Loading @@ -4198,6 +4197,12 @@ public class DevicePolicyManager { * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * 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. * 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 work profile and all policies set by the profile owner. * * @param flags Bit mask of additional options: currently supported flags are * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. Loading @@ -4205,10 +4210,7 @@ public class DevicePolicyManager { * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} */ */ public void wipeData(int flags) { public void wipeData(int flags) { throwIfParentInstance("wipeData"); wipeDataInternal(flags, ""); final String wipeReasonForUser = mContext.getString( R.string.work_profile_deleted_description_dpm_wipe); wipeDataInternal(flags, wipeReasonForUser); } } /** /** Loading @@ -4221,6 +4223,12 @@ public class DevicePolicyManager { * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * 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. * 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 work profile and all policies set by the profile owner. * * @param flags Bit mask of additional options: currently supported flags are * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and * {@link #WIPE_EUICC}. * {@link #WIPE_EUICC}. Loading @@ -4232,7 +4240,6 @@ public class DevicePolicyManager { * {@link #WIPE_SILENTLY} is set. * {@link #WIPE_SILENTLY} is set. */ */ public void wipeData(int flags, @NonNull CharSequence reason) { public void wipeData(int flags, @NonNull CharSequence reason) { throwIfParentInstance("wipeData"); Preconditions.checkNotNull(reason, "reason string is null"); Preconditions.checkNotNull(reason, "reason string is null"); Preconditions.checkStringNotEmpty(reason, "reason string is empty"); Preconditions.checkStringNotEmpty(reason, "reason string is empty"); Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set"); Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set"); Loading @@ -4250,7 +4257,7 @@ public class DevicePolicyManager { private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { if (mService != null) { if (mService != null) { try { try { mService.wipeDataWithReason(flags, wipeReasonForUser); mService.wipeDataWithReason(flags, wipeReasonForUser, mParentInstance); } catch (RemoteException e) { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer(); } } Loading Loading @@ -9287,6 +9294,12 @@ public class DevicePolicyManager { * <li>{@link #setRequiredStrongAuthTimeout}</li> * <li>{@link #setRequiredStrongAuthTimeout}</li> * </ul> * </ul> * * * <p>The following methods can be called by the profile owner of a managed profile * on an organization-owned device: * <ul> * <li>{@link #wipeData}</li> * </ul> * * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile. * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile. * @throws SecurityException if {@code admin} is not a profile owner. * @throws SecurityException if {@code admin} is not a profile owner. */ */ Loading core/java/android/app/admin/IDevicePolicyManager.aidl +1 −1 Original line number Original line Diff line number Diff line Loading @@ -102,7 +102,7 @@ interface IDevicePolicyManager { void lockNow(int flags, boolean parent); void lockNow(int flags, boolean parent); void wipeDataWithReason(int flags, String wipeReasonForUser); void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent); ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); ComponentName getGlobalProxyAdmin(int userHandle); ComponentName getGlobalProxyAdmin(int userHandle); Loading core/res/res/values/strings.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -404,6 +404,8 @@ <!-- Content text for a notification. The Title of the notification is "Work profile deleted", <!-- Content text for a notification. The Title of the notification is "Work profile deleted", This indicates that a work profile has been deleted because the maximum failed password attempts as been reached. [CHAR LIMIT=NONE]--> This indicates that a work profile has been deleted because the maximum failed password attempts as been reached. [CHAR LIMIT=NONE]--> <string name="work_profile_deleted_reason_maximum_password_failure">Too many password attempts</string> <string name="work_profile_deleted_reason_maximum_password_failure">Too many password attempts</string> <!-- Shows up as the reason for the work profile deletion when the admin of an organization-owend device relinquishes it. [CHAR LIMIT=NONE] --> <string name="device_ownership_relinquished">Admin relinquished device for personal use</string> <!-- Content title for a notification. This notification indicates that the device is managed <!-- Content title for a notification. This notification indicates that the device is managed and network logging was activated by a device owner. [CHAR LIMIT=NONE]--> and network logging was activated by a device owner. [CHAR LIMIT=NONE]--> Loading core/res/res/values/symbols.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -1193,6 +1193,7 @@ <java-symbol type="string" name="work_profile_deleted_details" /> <java-symbol type="string" name="work_profile_deleted_details" /> <java-symbol type="string" name="work_profile_deleted_description_dpm_wipe" /> <java-symbol type="string" name="work_profile_deleted_description_dpm_wipe" /> <java-symbol type="string" name="work_profile_deleted_reason_maximum_password_failure" /> <java-symbol type="string" name="work_profile_deleted_reason_maximum_password_failure" /> <java-symbol type="string" name="device_ownership_relinquished" /> <java-symbol type="string" name="network_logging_notification_title" /> <java-symbol type="string" name="network_logging_notification_title" /> <java-symbol type="string" name="network_logging_notification_text" /> <java-symbol type="string" name="network_logging_notification_text" /> <java-symbol type="string" name="factory_reset_warning" /> <java-symbol type="string" name="factory_reset_warning" /> Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +116 −10 Original line number Original line Diff line number Diff line Loading @@ -5568,6 +5568,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } } } private void enforceProfileOwnerOfCorpOwnedDevice(ActiveAdmin admin) { if (!isProfileOwnerOfOrganizationOwnedDevicte(admin)) { throw new SecurityException(String.format("Provided admin %s is either not a profile " + "owner or not on a corporate-owned device.", admin)); } } @Override @Override public boolean approveCaCert(String alias, int userId, boolean approval) { public boolean approveCaCert(String alias, int userId, boolean approval) { enforceManageUsers(); enforceManageUsers(); Loading Loading @@ -6613,27 +6620,83 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } @Override @Override public void wipeDataWithReason(int flags, String wipeReasonForUser) { public void wipeDataWithReason(int flags, String wipeReasonForUser, boolean calledOnParentInstance) { if (!mHasFeature) { if (!mHasFeature) { return; return; } } Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty"); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); final ActiveAdmin admin; final ActiveAdmin admin; synchronized (getLockObject()) { synchronized (getLockObject()) { admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); } } if (admin == null) { throw new SecurityException(String.format("No active admin for user %d", mInjector.userHandleGetCallingUserId())); } boolean calledByProfileOwnerOnOrgOwnedDevice = isProfileOwnerOfOrganizationOwnedDevicte(admin); if (calledOnParentInstance && !calledByProfileOwnerOnOrgOwnedDevice) { throw new SecurityException("Wiping the entire device can only be done by a profile" + "owner on organization-owned device."); } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if (!isDeviceOwner(admin) && !calledByProfileOwnerOnOrgOwnedDevice) { throw new SecurityException( "Only device owners or proflie owners of organization-owned device" + " can set WIPE_RESET_PROTECTION_DATA"); } } if (TextUtils.isEmpty(wipeReasonForUser)) { if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) { wipeReasonForUser = mContext.getString(R.string.device_ownership_relinquished); } else { wipeReasonForUser = mContext.getString( R.string.work_profile_deleted_description_dpm_wipe); } } int userId = admin.getUserHandle().getIdentifier(); if (calledByProfileOwnerOnOrgOwnedDevice) { // When wipeData is called on the parent instance, it implies wiping the entire device. if (calledOnParentInstance) { userId = UserHandle.USER_SYSTEM; } else { // when wipeData is _not_ called on the parent instance, it implies relinquishing // control over the device, wiping only the work profile. So the user restriction // on profile removal needs to be removed first. final long ident = mInjector.binderClearCallingIdentity(); try { // Clear restriction as user. mUserManager.setUserRestriction( UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false, UserHandle.SYSTEM); } finally { mInjector.binderRestoreCallingIdentity(ident); } } } DevicePolicyEventLogger DevicePolicyEventLogger .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON) .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON) .setAdmin(admin.info.getComponent()) .setAdmin(admin.info.getComponent()) .setInt(flags) .setInt(flags) .write(); .write(); String internalReason = "DevicePolicyManager.wipeDataWithReason() from " String internalReason = String.format( + admin.info.getComponent().flattenToShortString(); "DevicePolicyManager.wipeDataWithReason() from %s, organization-owned? %s", admin.info.getComponent().flattenToShortString(), calledByProfileOwnerOnOrgOwnedDevice); wipeDataNoLock( wipeDataNoLock( admin.info.getComponent(), flags, internalReason, wipeReasonForUser, admin.info.getComponent(), flags, internalReason, wipeReasonForUser, userId); admin.getUserHandle().getIdentifier()); } } private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, Loading @@ -6657,10 +6720,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if (!isDeviceOwner(admin, userId)) { throw new SecurityException( "Only device owner admins can set WIPE_RESET_PROTECTION_DATA"); } PersistentDataBlockManager manager = (PersistentDataBlockManager) PersistentDataBlockManager manager = (PersistentDataBlockManager) mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); if (manager != null) { if (manager != null) { Loading Loading @@ -7954,6 +8013,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } } } /** * Returns true if the provided {@code admin} is a profile owner and the profile is marked * as organization-owned. * The {@code admin} parameter must be obtained by the service by calling * {@code getActiveAdminForCallerLocked} or one of the similar variants, not caller-supplied * input. */ private boolean isProfileOwnerOfOrganizationOwnedDevicte(@Nullable ActiveAdmin admin) { if (admin == null) { return false; } final int adminUserId = admin.getUserHandle().getIdentifier(); if (!isProfileOwner(admin.info.getComponent(), adminUserId)) { Slog.w(LOG_TAG, String.format("%s is not profile owner of user %d", admin.info.getComponent(), adminUserId)); return false; } if (!canProfileOwnerAccessDeviceIds(adminUserId)) { Slog.w(LOG_TAG, String.format("Profile owner of user %d does not own the device.", adminUserId)); return false; } return true; } @Override @Override public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) { public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) { if (!mHasFeature) { if (!mHasFeature) { Loading Loading @@ -12614,6 +12702,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.i(LOG_TAG, String.format("Granting Device ID access to %s, for user %d", Slog.i(LOG_TAG, String.format("Granting Device ID access to %s, for user %d", who.flattenToString(), userId)); who.flattenToString(), userId)); // First, set restriction on removing the profile. final long ident = mInjector.binderClearCallingIdentity(); try { // Clear restriction as user. UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId)); if (!parentUser.isSystem()) { throw new IllegalStateException( String.format("Only the profile owner of a managed profile on the" + " primary user can be granted access to device identifiers, not" + " on user %d", parentUser.getIdentifier())); } mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true, parentUser); } finally { mInjector.binderRestoreCallingIdentity(ident); } // setProfileOwnerCanAccessDeviceIds will trigger writing of the profile owner // setProfileOwnerCanAccessDeviceIds will trigger writing of the profile owner // data, no need to do it manually. // data, no need to do it manually. mOwners.setProfileOwnerCanAccessDeviceIds(userId); mOwners.setProfileOwnerCanAccessDeviceIds(userId); Loading
core/java/android/app/admin/DevicePolicyManager.java +20 −7 Original line number Original line Diff line number Diff line Loading @@ -88,7 +88,6 @@ import android.telephony.data.ApnSetting; import android.util.ArraySet; import android.util.ArraySet; import android.util.Log; import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions; Loading Loading @@ -4198,6 +4197,12 @@ public class DevicePolicyManager { * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * 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. * 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 work profile and all policies set by the profile owner. * * @param flags Bit mask of additional options: currently supported flags are * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA}, * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}. Loading @@ -4205,10 +4210,7 @@ public class DevicePolicyManager { * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} */ */ public void wipeData(int flags) { public void wipeData(int flags) { throwIfParentInstance("wipeData"); wipeDataInternal(flags, ""); final String wipeReasonForUser = mContext.getString( R.string.work_profile_deleted_description_dpm_wipe); wipeDataInternal(flags, wipeReasonForUser); } } /** /** Loading @@ -4221,6 +4223,12 @@ public class DevicePolicyManager { * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * 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. * 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 work profile and all policies set by the profile owner. * * @param flags Bit mask of additional options: currently supported flags are * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and * {@link #WIPE_EUICC}. * {@link #WIPE_EUICC}. Loading @@ -4232,7 +4240,6 @@ public class DevicePolicyManager { * {@link #WIPE_SILENTLY} is set. * {@link #WIPE_SILENTLY} is set. */ */ public void wipeData(int flags, @NonNull CharSequence reason) { public void wipeData(int flags, @NonNull CharSequence reason) { throwIfParentInstance("wipeData"); Preconditions.checkNotNull(reason, "reason string is null"); Preconditions.checkNotNull(reason, "reason string is null"); Preconditions.checkStringNotEmpty(reason, "reason string is empty"); Preconditions.checkStringNotEmpty(reason, "reason string is empty"); Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set"); Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set"); Loading @@ -4250,7 +4257,7 @@ public class DevicePolicyManager { private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { if (mService != null) { if (mService != null) { try { try { mService.wipeDataWithReason(flags, wipeReasonForUser); mService.wipeDataWithReason(flags, wipeReasonForUser, mParentInstance); } catch (RemoteException e) { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer(); } } Loading Loading @@ -9287,6 +9294,12 @@ public class DevicePolicyManager { * <li>{@link #setRequiredStrongAuthTimeout}</li> * <li>{@link #setRequiredStrongAuthTimeout}</li> * </ul> * </ul> * * * <p>The following methods can be called by the profile owner of a managed profile * on an organization-owned device: * <ul> * <li>{@link #wipeData}</li> * </ul> * * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile. * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile. * @throws SecurityException if {@code admin} is not a profile owner. * @throws SecurityException if {@code admin} is not a profile owner. */ */ Loading
core/java/android/app/admin/IDevicePolicyManager.aidl +1 −1 Original line number Original line Diff line number Diff line Loading @@ -102,7 +102,7 @@ interface IDevicePolicyManager { void lockNow(int flags, boolean parent); void lockNow(int flags, boolean parent); void wipeDataWithReason(int flags, String wipeReasonForUser); void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent); ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); ComponentName getGlobalProxyAdmin(int userHandle); ComponentName getGlobalProxyAdmin(int userHandle); Loading
core/res/res/values/strings.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -404,6 +404,8 @@ <!-- Content text for a notification. The Title of the notification is "Work profile deleted", <!-- Content text for a notification. The Title of the notification is "Work profile deleted", This indicates that a work profile has been deleted because the maximum failed password attempts as been reached. [CHAR LIMIT=NONE]--> This indicates that a work profile has been deleted because the maximum failed password attempts as been reached. [CHAR LIMIT=NONE]--> <string name="work_profile_deleted_reason_maximum_password_failure">Too many password attempts</string> <string name="work_profile_deleted_reason_maximum_password_failure">Too many password attempts</string> <!-- Shows up as the reason for the work profile deletion when the admin of an organization-owend device relinquishes it. [CHAR LIMIT=NONE] --> <string name="device_ownership_relinquished">Admin relinquished device for personal use</string> <!-- Content title for a notification. This notification indicates that the device is managed <!-- Content title for a notification. This notification indicates that the device is managed and network logging was activated by a device owner. [CHAR LIMIT=NONE]--> and network logging was activated by a device owner. [CHAR LIMIT=NONE]--> Loading
core/res/res/values/symbols.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -1193,6 +1193,7 @@ <java-symbol type="string" name="work_profile_deleted_details" /> <java-symbol type="string" name="work_profile_deleted_details" /> <java-symbol type="string" name="work_profile_deleted_description_dpm_wipe" /> <java-symbol type="string" name="work_profile_deleted_description_dpm_wipe" /> <java-symbol type="string" name="work_profile_deleted_reason_maximum_password_failure" /> <java-symbol type="string" name="work_profile_deleted_reason_maximum_password_failure" /> <java-symbol type="string" name="device_ownership_relinquished" /> <java-symbol type="string" name="network_logging_notification_title" /> <java-symbol type="string" name="network_logging_notification_title" /> <java-symbol type="string" name="network_logging_notification_text" /> <java-symbol type="string" name="network_logging_notification_text" /> <java-symbol type="string" name="factory_reset_warning" /> <java-symbol type="string" name="factory_reset_warning" /> Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +116 −10 Original line number Original line Diff line number Diff line Loading @@ -5568,6 +5568,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } } } private void enforceProfileOwnerOfCorpOwnedDevice(ActiveAdmin admin) { if (!isProfileOwnerOfOrganizationOwnedDevicte(admin)) { throw new SecurityException(String.format("Provided admin %s is either not a profile " + "owner or not on a corporate-owned device.", admin)); } } @Override @Override public boolean approveCaCert(String alias, int userId, boolean approval) { public boolean approveCaCert(String alias, int userId, boolean approval) { enforceManageUsers(); enforceManageUsers(); Loading Loading @@ -6613,27 +6620,83 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } @Override @Override public void wipeDataWithReason(int flags, String wipeReasonForUser) { public void wipeDataWithReason(int flags, String wipeReasonForUser, boolean calledOnParentInstance) { if (!mHasFeature) { if (!mHasFeature) { return; return; } } Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty"); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); final ActiveAdmin admin; final ActiveAdmin admin; synchronized (getLockObject()) { synchronized (getLockObject()) { admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); } } if (admin == null) { throw new SecurityException(String.format("No active admin for user %d", mInjector.userHandleGetCallingUserId())); } boolean calledByProfileOwnerOnOrgOwnedDevice = isProfileOwnerOfOrganizationOwnedDevicte(admin); if (calledOnParentInstance && !calledByProfileOwnerOnOrgOwnedDevice) { throw new SecurityException("Wiping the entire device can only be done by a profile" + "owner on organization-owned device."); } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if (!isDeviceOwner(admin) && !calledByProfileOwnerOnOrgOwnedDevice) { throw new SecurityException( "Only device owners or proflie owners of organization-owned device" + " can set WIPE_RESET_PROTECTION_DATA"); } } if (TextUtils.isEmpty(wipeReasonForUser)) { if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) { wipeReasonForUser = mContext.getString(R.string.device_ownership_relinquished); } else { wipeReasonForUser = mContext.getString( R.string.work_profile_deleted_description_dpm_wipe); } } int userId = admin.getUserHandle().getIdentifier(); if (calledByProfileOwnerOnOrgOwnedDevice) { // When wipeData is called on the parent instance, it implies wiping the entire device. if (calledOnParentInstance) { userId = UserHandle.USER_SYSTEM; } else { // when wipeData is _not_ called on the parent instance, it implies relinquishing // control over the device, wiping only the work profile. So the user restriction // on profile removal needs to be removed first. final long ident = mInjector.binderClearCallingIdentity(); try { // Clear restriction as user. mUserManager.setUserRestriction( UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false, UserHandle.SYSTEM); } finally { mInjector.binderRestoreCallingIdentity(ident); } } } DevicePolicyEventLogger DevicePolicyEventLogger .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON) .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON) .setAdmin(admin.info.getComponent()) .setAdmin(admin.info.getComponent()) .setInt(flags) .setInt(flags) .write(); .write(); String internalReason = "DevicePolicyManager.wipeDataWithReason() from " String internalReason = String.format( + admin.info.getComponent().flattenToShortString(); "DevicePolicyManager.wipeDataWithReason() from %s, organization-owned? %s", admin.info.getComponent().flattenToShortString(), calledByProfileOwnerOnOrgOwnedDevice); wipeDataNoLock( wipeDataNoLock( admin.info.getComponent(), flags, internalReason, wipeReasonForUser, admin.info.getComponent(), flags, internalReason, wipeReasonForUser, userId); admin.getUserHandle().getIdentifier()); } } private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, Loading @@ -6657,10 +6720,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if (!isDeviceOwner(admin, userId)) { throw new SecurityException( "Only device owner admins can set WIPE_RESET_PROTECTION_DATA"); } PersistentDataBlockManager manager = (PersistentDataBlockManager) PersistentDataBlockManager manager = (PersistentDataBlockManager) mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); if (manager != null) { if (manager != null) { Loading Loading @@ -7954,6 +8013,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } } } /** * Returns true if the provided {@code admin} is a profile owner and the profile is marked * as organization-owned. * The {@code admin} parameter must be obtained by the service by calling * {@code getActiveAdminForCallerLocked} or one of the similar variants, not caller-supplied * input. */ private boolean isProfileOwnerOfOrganizationOwnedDevicte(@Nullable ActiveAdmin admin) { if (admin == null) { return false; } final int adminUserId = admin.getUserHandle().getIdentifier(); if (!isProfileOwner(admin.info.getComponent(), adminUserId)) { Slog.w(LOG_TAG, String.format("%s is not profile owner of user %d", admin.info.getComponent(), adminUserId)); return false; } if (!canProfileOwnerAccessDeviceIds(adminUserId)) { Slog.w(LOG_TAG, String.format("Profile owner of user %d does not own the device.", adminUserId)); return false; } return true; } @Override @Override public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) { public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) { if (!mHasFeature) { if (!mHasFeature) { Loading Loading @@ -12614,6 +12702,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.i(LOG_TAG, String.format("Granting Device ID access to %s, for user %d", Slog.i(LOG_TAG, String.format("Granting Device ID access to %s, for user %d", who.flattenToString(), userId)); who.flattenToString(), userId)); // First, set restriction on removing the profile. final long ident = mInjector.binderClearCallingIdentity(); try { // Clear restriction as user. UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId)); if (!parentUser.isSystem()) { throw new IllegalStateException( String.format("Only the profile owner of a managed profile on the" + " primary user can be granted access to device identifiers, not" + " on user %d", parentUser.getIdentifier())); } mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true, parentUser); } finally { mInjector.binderRestoreCallingIdentity(ident); } // setProfileOwnerCanAccessDeviceIds will trigger writing of the profile owner // setProfileOwnerCanAccessDeviceIds will trigger writing of the profile owner // data, no need to do it manually. // data, no need to do it manually. mOwners.setProfileOwnerCanAccessDeviceIds(userId); mOwners.setProfileOwnerCanAccessDeviceIds(userId);