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

Commit 804d53c6 authored by Eran Messeri's avatar Eran Messeri Committed by Android (Google) Code Review
Browse files

Merge "Wiping and relinquishing org-owned devices"

parents d5617a96 b8c46e08
Loading
Loading
Loading
Loading
+20 −7
Original line number Original line Diff line number Diff line
@@ -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;
@@ -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}.
@@ -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);
    }
    }


    /**
    /**
@@ -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}.
@@ -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");
@@ -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();
            }
            }
@@ -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.
     */
     */
+1 −1
Original line number Original line Diff line number Diff line
@@ -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);
+2 −0
Original line number Original line Diff line number Diff line
@@ -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]-->
+1 −0
Original line number Original line Diff line number Diff line
@@ -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" />
+116 −10
Original line number Original line Diff line number Diff line
@@ -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();
@@ -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,
@@ -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) {
@@ -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) {
@@ -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