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

Commit 101b2f5e authored by Rubin Xu's avatar Rubin Xu
Browse files

Enable coexistable device policy APIs for non-EMM management

Make DPM.getRequiredPasswordComplexity fully coexistable.

Bug: 335624297
Test: PasswordComplexityTest
      PasswordTest
      ManagedProfilePasswordTest
      FrameworksServicesTests:DevicePolicyManagerTest

Change-Id: I6e8ad8ee3389c9f779f6f04e859a0f22d5fbac29
parent 9a094858
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -5842,6 +5842,24 @@ public class DevicePolicyManager {
     * with {@link #PASSWORD_QUALITY_UNSPECIFIED} on that instance prior to setting complexity
     * requirement for the managed profile.
     *
     * Starting from {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, after the password
     * requirement has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
     * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
     * successfully set or not. This callback will contain:
     * <ul>
     * <li> The policy identifier {@link DevicePolicyIdentifiers#PASSWORD_COMPLEXITY_POLICY}
     * <li> The {@link TargetUser} that this policy relates to
     * <li> The {@link PolicyUpdateResult}, which will be
     * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
     * reason the policy failed to be set
     * e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
     * </ul>
     * If there has been a change to the policy,
     * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
     * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
     * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
     * will contain the reason why the policy changed.
     *
     * @throws SecurityException if the calling application is not a device owner or a profile
     * owner.
     * @throws IllegalArgumentException if the complexity level is not one of the four above.
@@ -5849,6 +5867,7 @@ public class DevicePolicyManager {
     * are password requirements specified using {@link #setPasswordQuality(ComponentName, int)}
     * on the parent {@code DevicePolicyManager} instance.
     */
    @SupportsCoexistence
    @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, conditional = true)
    public void setRequiredPasswordComplexity(@PasswordComplexity int passwordComplexity) {
        if (mService == null) {
@@ -5880,6 +5899,7 @@ public class DevicePolicyManager {
     * owner.
     */
    @PasswordComplexity
    @SupportsCoexistence
    @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, conditional = true)
    public int getRequiredPasswordComplexity() {
        if (mService == null) {
+10 −0
Original line number Diff line number Diff line
@@ -327,6 +327,16 @@ flag {
  }
}

flag {
    name: "unmanaged_mode_migration"
    namespace: "enterprise"
    description: "Migrate APIs for unmanaged mode"
    bug: "335624297"
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "headless_single_user_fixes"
    namespace: "enterprise"
+76 −2
Original line number Diff line number Diff line
@@ -78,6 +78,10 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -688,6 +692,12 @@ final class DevicePolicyEngine {
     */
    @Nullable
    <V> V getResolvedPolicy(@NonNull PolicyDefinition<V> policyDefinition, int userId) {
        PolicyValue<V> resolvedValue = getResolvedPolicyValue(policyDefinition, userId);
        return resolvedValue == null ? null : resolvedValue.getValue();
    }

    private <V> PolicyValue<V> getResolvedPolicyValue(@NonNull PolicyDefinition<V> policyDefinition,
            int userId) {
        Objects.requireNonNull(policyDefinition);

        synchronized (mLock) {
@@ -699,8 +709,36 @@ final class DevicePolicyEngine {
                resolvedValue = getGlobalPolicyStateLocked(
                        policyDefinition).getCurrentResolvedPolicy();
            }
            return resolvedValue == null ? null : resolvedValue.getValue();
            return resolvedValue;
        }
    }

    /**
     * Retrieves resolved policy for the provided {@code policyDefinition} and a list of
     * users.
     */
    @Nullable
    <V> V getResolvedPolicyAcrossUsers(@NonNull PolicyDefinition<V> policyDefinition,
            List<Integer> users) {
        Objects.requireNonNull(policyDefinition);

        List<PolicyValue<V>> adminPolicies = new ArrayList<>();
        synchronized (mLock) {
            for (int userId : users) {
                PolicyValue<V> resolvedValue = getResolvedPolicyValue(policyDefinition, userId);
                if (resolvedValue != null) {
                    adminPolicies.add(resolvedValue);
                }
            }
        }
        // We will be aggregating PolicyValue across multiple admins across multiple users,
        // including different policies set by the same admin on different users. This is
        // not supported by ResolutionMechanism generically, instead we need to call the special
        // resolve() method that doesn't care about admins who set the policy. Note that not every
        // ResolutionMechanism supports this.
        PolicyValue<V> resolvedValue =
                policyDefinition.getResolutionMechanism().resolve(adminPolicies);
        return resolvedValue == null ? null : resolvedValue.getValue();
    }

    /**
@@ -1743,6 +1781,18 @@ final class DevicePolicyEngine {
        }
    }

    /**
     * Create a backup of the policy engine XML file, so that we can recover previous state
     * in case some data-loss bug is triggered e.g. during migration.
     *
     * Backup is only created if one with the same ID does not exist yet.
     */
    void createBackup(String backupId) {
        synchronized (mLock) {
            DevicePoliciesReaderWriter.createBackup(backupId);
        }
    }

    <V> void reapplyAllPoliciesOnBootLocked() {
        for (PolicyKey policy : mGlobalPolicies.keySet()) {
            PolicyState<?> policyState = mGlobalPolicies.get(policy);
@@ -1822,6 +1872,8 @@ final class DevicePolicyEngine {

    private class DevicePoliciesReaderWriter {
        private static final String DEVICE_POLICIES_XML = "device_policy_state.xml";
        private static final String BACKUP_DIRECTORY = "device_policy_backups";
        private static final String BACKUP_FILENAME = "device_policy_state.%s.xml";
        private static final String TAG_LOCAL_POLICY_ENTRY = "local-policy-entry";
        private static final String TAG_GLOBAL_POLICY_ENTRY = "global-policy-entry";
        private static final String TAG_POLICY_STATE_ENTRY = "policy-state-entry";
@@ -1836,8 +1888,30 @@ final class DevicePolicyEngine {

        private final File mFile;

        private static File getFileName() {
            return new File(Environment.getDataSystemDirectory(), DEVICE_POLICIES_XML);
        }
        private DevicePoliciesReaderWriter() {
            mFile = new File(Environment.getDataSystemDirectory(), DEVICE_POLICIES_XML);
            mFile = getFileName();
        }

        public static void createBackup(String backupId) {
            try {
                File backupDirectory = new File(Environment.getDataSystemDirectory(),
                        BACKUP_DIRECTORY);
                backupDirectory.mkdir();
                Path backupPath = Path.of(backupDirectory.getPath(),
                        BACKUP_FILENAME.formatted(backupId));
                if (backupPath.toFile().exists()) {
                    Log.w(TAG, "Backup already exist: " + backupPath);
                } else {
                    Files.copy(getFileName().toPath(), backupPath,
                            StandardCopyOption.REPLACE_EXISTING);
                    Log.i(TAG, "Backup created at " + backupPath);
                }
            } catch (Exception e) {
                Log.e(TAG, "Cannot create backup " + backupId, e);
            }
        }

        void writeToFileLocked() {
+272 −19

File changed.

Preview size limit exceeded, changes collapsed.

+8 −5
Original line number Diff line number Diff line
@@ -19,9 +19,9 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.app.admin.PolicyValue;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

final class MostRestrictive<V> extends ResolutionMechanism<V> {

@@ -33,18 +33,21 @@ final class MostRestrictive<V> extends ResolutionMechanism<V> {

    @Override
    PolicyValue<V> resolve(@NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminPolicies) {
        return resolve(new ArrayList<>(adminPolicies.values()));
    }

    @Override
    PolicyValue<V> resolve(List<PolicyValue<V>> adminPolicies) {
        if (adminPolicies.isEmpty()) {
            return null;
        }
        for (PolicyValue<V> value : mMostToLeastRestrictive) {
            if (adminPolicies.containsValue(value)) {
            if (adminPolicies.contains(value)) {
                return value;
            }
        }
        // Return first set policy if none can be found in known values
        Map.Entry<EnforcingAdmin, PolicyValue<V>> policy =
                adminPolicies.entrySet().stream().findFirst().get();
        return policy.getValue();
        return adminPolicies.get(0);
    }

    @Override
Loading