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

Commit 3c8d8c6f authored by Kholoud Mohamed's avatar Kholoud Mohamed
Browse files

Allow non-DPC admins to use DeviceAdminService

Bug: 261432333
Bug: 232918480
Test: manual
Test: atest com.android.cts.devicepolicy.DeviceAdminServiceDeviceOwnerTest
Test: atest com.android.cts.devicepolicy.DeviceAdminServiceProfileOwnerTest
Change-Id: I6c57458fcb9823e58381bb278d90e7673a576789
parent d4e5ca03
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.Intent;
import android.os.IBinder;
import android.os.IBinder;


// TODO(b/263363091): Restrict to DPC and holders of a new role permission and update javadocs
/**
/**
 * Base class for a service that device owner/profile owners can optionally have.
 * Base class for a service that device owner/profile owners can optionally have.
 *
 *
@@ -45,6 +46,11 @@ import android.os.IBinder;
 *
 *
 * <p>Note the process may still be killed if the system is under heavy memory pressure, in which
 * <p>Note the process may still be killed if the system is under heavy memory pressure, in which
 * case the process will be re-started later.
 * case the process will be re-started later.
 *
 * <p>Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
 * non-DPC admins can also optionally implement this service using the details
 * mentioned above to ensure they receive policy update broadcasts
 * (see {@link PolicyUpdatesReceiver}).
 */
 */
public class DeviceAdminService extends Service {
public class DeviceAdminService extends Service {
    private final IDeviceAdminServiceImpl mImpl;
    private final IDeviceAdminServiceImpl mImpl;
+4 −1
Original line number Original line Diff line number Diff line
@@ -32,7 +32,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.Objects;


// TODO(b/261432333): Add more detailed javadocs on using DeviceAdminService.
/**
/**
 * Base class for implementing a policy update receiver. This class provides a convenience for
 * Base class for implementing a policy update receiver. This class provides a convenience for
 * interpreting the raw intent actions ({@link #ACTION_DEVICE_POLICY_SET_RESULT} and
 * interpreting the raw intent actions ({@link #ACTION_DEVICE_POLICY_SET_RESULT} and
@@ -43,6 +42,10 @@ import java.util.Objects;
 *
 *
 * <p>When publishing your {@code PolicyUpdatesReceiver} subclass as a receiver, it must
 * <p>When publishing your {@code PolicyUpdatesReceiver} subclass as a receiver, it must
 * require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission.
 * require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission.
 *
 * <p>Admins can implement {@link DeviceAdminService} to ensure they receive all policy updates
 * (for policies they have set) via {@link #onPolicyChanged} by constantly being bound to by the
 * system. For more information see {@link DeviceAdminService}.
 */
 */
public abstract class PolicyUpdatesReceiver extends BroadcastReceiver {
public abstract class PolicyUpdatesReceiver extends BroadcastReceiver {
    private static String TAG = "PolicyUpdatesReceiver";
    private static String TAG = "PolicyUpdatesReceiver";
+79 −22
Original line number Original line Diff line number Diff line
@@ -35,8 +35,11 @@ import com.android.server.am.PersistentConnection;
import com.android.server.appbinding.AppBindingUtils;
import com.android.server.appbinding.AppBindingUtils;
import com.android.server.utils.Slogf;
import com.android.server.utils.Slogf;


import java.util.HashMap;
import java.util.Map;

/**
/**
 * Manages connections to persistent services in owner packages.
 * Manages connections to persistent services in admin packages.
 */
 */
public class DeviceAdminServiceController {
public class DeviceAdminServiceController {
    static final String TAG = DevicePolicyManagerService.LOG_TAG;
    static final String TAG = DevicePolicyManagerService.LOG_TAG;
@@ -76,7 +79,8 @@ public class DeviceAdminServiceController {
     * User-ID -> {@link PersistentConnection}.
     * User-ID -> {@link PersistentConnection}.
     */
     */
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final SparseArray<DevicePolicyServiceConnection> mConnections = new SparseArray<>();
    private final SparseArray<Map<String, DevicePolicyServiceConnection>> mConnections =
            new SparseArray<>();


    public DeviceAdminServiceController(DevicePolicyManagerService service,
    public DeviceAdminServiceController(DevicePolicyManagerService service,
            DevicePolicyConstants constants) {
            DevicePolicyConstants constants) {
@@ -104,9 +108,9 @@ public class DeviceAdminServiceController {


    /**
    /**
     * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
     * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
     * in an owner package and connect to it.
     * in an admin package and connect to it.
     */
     */
    public void startServiceForOwner(@NonNull String packageName, int userId,
    public void startServiceForAdmin(@NonNull String packageName, int userId,
            @NonNull String actionForLog) {
            @NonNull String actionForLog) {
        final long token = mInjector.binderClearCallingIdentity();
        final long token = mInjector.binderClearCallingIdentity();
        try {
        try {
@@ -114,15 +118,16 @@ public class DeviceAdminServiceController {
                final ServiceInfo service = findService(packageName, userId);
                final ServiceInfo service = findService(packageName, userId);
                if (service == null) {
                if (service == null) {
                    if (DEBUG) {
                    if (DEBUG) {
                        Slogf.d(TAG, "Owner package %s on u%d has no service.", packageName,
                        Slogf.d(TAG, "Admin package %s on u%d has no service.", packageName,
                                userId);
                                userId);
                    }
                    }
                    disconnectServiceOnUserLocked(userId, actionForLog);
                    disconnectServiceOnUserLocked(packageName, userId, actionForLog);
                    return;
                    return;
                }
                }
                // See if it's already running.
                // See if it's already running.
                final PersistentConnection<IDeviceAdminService> existing =
                final PersistentConnection<IDeviceAdminService> existing =
                        mConnections.get(userId);
                        mConnections.contains(userId)
                                ? mConnections.get(userId).get(packageName) : null;
                if (existing != null) {
                if (existing != null) {
                    // Note even when we're already connected to the same service, the binding
                    // Note even when we're already connected to the same service, the binding
                    // would have died at this point due to a package update.  So we disconnect
                    // would have died at this point due to a package update.  So we disconnect
@@ -131,18 +136,21 @@ public class DeviceAdminServiceController {
                        Slogf.d("Disconnecting from existing service connection.", packageName,
                        Slogf.d("Disconnecting from existing service connection.", packageName,
                                userId);
                                userId);
                    }
                    }
                    disconnectServiceOnUserLocked(userId, actionForLog);
                    disconnectServiceOnUserLocked(packageName, userId, actionForLog);
                }
                }


                if (DEBUG) {
                if (DEBUG) {
                    Slogf.d("Owner package %s on u%d has service %s for %s", packageName, userId,
                    Slogf.d("Admin package %s on u%d has service %s for %s", packageName, userId,
                        service.getComponentName().flattenToShortString(), actionForLog);
                        service.getComponentName().flattenToShortString(), actionForLog);
                }
                }


                final DevicePolicyServiceConnection conn =
                final DevicePolicyServiceConnection conn =
                        new DevicePolicyServiceConnection(
                        new DevicePolicyServiceConnection(
                                userId, service.getComponentName());
                                userId, service.getComponentName());
                mConnections.put(userId, conn);
                if (!mConnections.contains(userId)) {
                    mConnections.put(userId, new HashMap<>());
                }
                mConnections.get(userId).put(packageName, conn);
                conn.bind();
                conn.bind();
            }
            }
        } finally {
        } finally {
@@ -151,9 +159,24 @@ public class DeviceAdminServiceController {
    }
    }


    /**
    /**
     * Stop an owner service on a given user.
     * Stop an admin service on a given user.
     */
    public void stopServiceForAdmin(
            @NonNull String packageName, int userId, @NonNull String actionForLog) {
        final long token = mInjector.binderClearCallingIdentity();
        try {
            synchronized (mLock) {
                disconnectServiceOnUserLocked(packageName, userId, actionForLog);
            }
        } finally {
            mInjector.binderRestoreCallingIdentity(token);
        }
    }

    /**
     * Stop all admin services on a given user.
     */
     */
    public void stopServiceForOwner(int userId, @NonNull String actionForLog) {
    public void stopServicesForUser(int userId, @NonNull String actionForLog) {
        final long token = mInjector.binderClearCallingIdentity();
        final long token = mInjector.binderClearCallingIdentity();
        try {
        try {
            synchronized (mLock) {
            synchronized (mLock) {
@@ -165,17 +188,43 @@ public class DeviceAdminServiceController {
    }
    }


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
    private void disconnectServiceOnUserLocked(
        final DevicePolicyServiceConnection conn = mConnections.get(userId);
            @NonNull String packageName, int userId, @NonNull String actionForLog) {
        final DevicePolicyServiceConnection conn = mConnections.contains(userId)
                ? mConnections.get(userId).get(packageName) : null;
        if (conn != null) {
        if (conn != null) {
            if (DEBUG) {
            if (DEBUG) {
                Slogf.d(TAG, "Stopping service for u%d if already running for %s.", userId,
                Slogf.d(TAG, "Stopping service for package %s on u%d if already running for %s.",
                        packageName,
                        userId,
                        actionForLog);
                        actionForLog);
            }
            }
            conn.unbind();
            conn.unbind();
            mConnections.get(userId).remove(packageName);
            if (mConnections.get(userId).isEmpty()) {
                mConnections.remove(userId);
                mConnections.remove(userId);
            }
            }
        }
        }
    }

    @GuardedBy("mLock")
    private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
        if (!mConnections.contains(userId)) {
            return;
        }
        for (String packageName : mConnections.get(userId).keySet()) {
            DevicePolicyServiceConnection conn = mConnections.get(userId).get(packageName);
            if (DEBUG) {
                Slogf.d(TAG,
                        "Stopping service for package %s on u%d if already running for %s.",
                        packageName,
                        userId,
                        actionForLog);
            }
            conn.unbind();
        }
        mConnections.remove(userId);
    }


    /** dump content */
    /** dump content */
    public void dump(IndentingPrintWriter pw) {
    public void dump(IndentingPrintWriter pw) {
@@ -183,16 +232,24 @@ public class DeviceAdminServiceController {
            if (mConnections.size() == 0) {
            if (mConnections.size() == 0) {
                return;
                return;
            }
            }
            pw.println("Owner Services:");
            pw.println("Admin Services:");
            pw.increaseIndent();
            pw.increaseIndent();
            for (int i = 0; i < mConnections.size(); i++) {
            for (int i = 0; i < mConnections.size(); i++) {
                final int userId = mConnections.keyAt(i);
                final int userId = mConnections.keyAt(i);
                pw.print("User: "); pw.println(userId);
                pw.print("User: ");
                pw.println(userId);
                for (String packageName : mConnections.get(userId).keySet()) {
                    pw.increaseIndent();
                    pw.print("Package: ");
                    pw.println(packageName);


                final DevicePolicyServiceConnection con = mConnections.valueAt(i);
                    final DevicePolicyServiceConnection con = mConnections.valueAt(i)
                            .get(packageName);
                    pw.increaseIndent();
                    pw.increaseIndent();
                    con.dump("", pw);
                    con.dump("", pw);
                    pw.decreaseIndent();
                    pw.decreaseIndent();
                    pw.decreaseIndent();
                }
            }
            }
            pw.decreaseIndent();
            pw.decreaseIndent();
        }
        }
+209 −1
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ import static android.app.admin.PolicyUpdatesReceiver.POLICY_SET_RESULT_SUCCESS;
import android.Manifest;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PolicyUpdatesReceiver;
import android.app.admin.PolicyUpdatesReceiver;
import android.app.admin.TargetUser;
import android.app.admin.TargetUser;
import android.content.Context;
import android.content.Context;
@@ -57,7 +58,9 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.Objects;
@@ -86,11 +89,22 @@ final class DevicePolicyEngine {
     */
     */
    private final Map<String, PolicyState<?>> mGlobalPolicies;
    private final Map<String, PolicyState<?>> mGlobalPolicies;


    DevicePolicyEngine(@NonNull Context context) {
    /**
     * Map containing the current set of admins in each user with active policies.
     */
    private final SparseArray<Set<EnforcingAdmin>> mEnforcingAdmins;

    private final DeviceAdminServiceController mDeviceAdminServiceController;

    DevicePolicyEngine(
            @NonNull Context context,
            @NonNull DeviceAdminServiceController deviceAdminServiceController) {
        mContext = Objects.requireNonNull(context);
        mContext = Objects.requireNonNull(context);
        mDeviceAdminServiceController = Objects.requireNonNull(deviceAdminServiceController);
        mUserManager = mContext.getSystemService(UserManager.class);
        mUserManager = mContext.getSystemService(UserManager.class);
        mLocalPolicies = new SparseArray<>();
        mLocalPolicies = new SparseArray<>();
        mGlobalPolicies = new HashMap<>();
        mGlobalPolicies = new HashMap<>();
        mEnforcingAdmins = new SparseArray<>();
    }
    }


    // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
    // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
@@ -137,6 +151,8 @@ final class DevicePolicyEngine {
                    REASON_CONFLICTING_ADMIN_POLICY,
                    REASON_CONFLICTING_ADMIN_POLICY,
                    userId);
                    userId);


            updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);

            write();
            write();
        }
        }
    }
    }
@@ -187,6 +203,8 @@ final class DevicePolicyEngine {
                removeLocalPolicyStateLocked(policyDefinition, userId);
                removeLocalPolicyStateLocked(policyDefinition, userId);
            }
            }


            updateDeviceAdminServiceOnPolicyRemoveLocked(enforcingAdmin);

            write();
            write();
        }
        }
    }
    }
@@ -257,6 +275,8 @@ final class DevicePolicyEngine {
                    REASON_CONFLICTING_ADMIN_POLICY,
                    REASON_CONFLICTING_ADMIN_POLICY,
                    UserHandle.USER_ALL);
                    UserHandle.USER_ALL);


            updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);

            write();
            write();
        }
        }
    }
    }
@@ -298,6 +318,8 @@ final class DevicePolicyEngine {
                removeGlobalPolicyStateLocked(policyDefinition);
                removeGlobalPolicyStateLocked(policyDefinition);
            }
            }


            updateDeviceAdminServiceOnPolicyRemoveLocked(enforcingAdmin);

            write();
            write();
        }
        }
    }
    }
@@ -580,6 +602,7 @@ final class DevicePolicyEngine {
                getTargetUser(admin.getUserId(), userId));
                getTargetUser(admin.getUserId(), userId));
        extras.putInt(EXTRA_POLICY_UPDATE_REASON_KEY, reason);
        extras.putInt(EXTRA_POLICY_UPDATE_REASON_KEY, reason);
        intent.putExtras(extras);
        intent.putExtras(extras);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);


        maybeSendIntentToAdminReceivers(
        maybeSendIntentToAdminReceivers(
                intent, UserHandle.of(admin.getUserId()), receivers);
                intent, UserHandle.of(admin.getUserId()), receivers);
@@ -622,6 +645,164 @@ final class DevicePolicyEngine {
        });
        });
    }
    }


    /**
     * Starts/Stops the services that handle {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE}
     * in the enforcing admins for the given {@code userId}.
     */
    private void updateDeviceAdminsServicesForUser(
            int userId, boolean enable, @NonNull String actionForLog) {
        if (!enable) {
            mDeviceAdminServiceController.stopServicesForUser(
                    userId, actionForLog);
        } else {
            for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) {
                // DPCs are handled separately in DPMS, no need to reestablish the connection here.
                if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
                    continue;
                }
                mDeviceAdminServiceController.startServiceForAdmin(
                        admin.getPackageName(), userId, actionForLog);
            }
        }
    }

    /**
     * Handles internal state related to a user getting started.
     */
    void handleStartUser(int userId) {
        updateDeviceAdminsServicesForUser(
                userId, /* enable= */ true, /* actionForLog= */ "start-user");
    }

    /**
     * Handles internal state related to a user getting started.
     */
    void handleUnlockUser(int userId) {
        updateDeviceAdminsServicesForUser(
                userId, /* enable= */ true, /* actionForLog= */ "unlock-user");
    }

    /**
     * Handles internal state related to a user getting stopped.
     */
    void handleStopUser(int userId) {
        updateDeviceAdminsServicesForUser(
                userId, /* enable= */ false, /* actionForLog= */ "stop-user");
    }

    /**
     * Handles internal state related to packages getting updated.
     */
    void handlePackageChanged(@Nullable String updatedPackage, int userId) {
        if (updatedPackage == null) {
            return;
        }
        updateDeviceAdminServiceOnPackageChanged(updatedPackage, userId);
    }

    /**
     * Reestablishes the service that handles
     * {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} in the enforcing admin if the package
     * was updated, as a package update results in the persistent connection getting reset.
     */
    private void updateDeviceAdminServiceOnPackageChanged(
            @NonNull String updatedPackage, int userId) {
        for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) {
            // DPCs are handled separately in DPMS, no need to reestablish the connection here.
            if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
                continue;
            }
            if (updatedPackage.equals(admin.getPackageName())) {
                mDeviceAdminServiceController.startServiceForAdmin(
                        updatedPackage, userId, /* actionForLog= */ "package-broadcast");
            }
        }
    }

    /**
     * Called after an admin policy has been added to start binding to the admin if a connection
     * was not already established.
     */
    private void updateDeviceAdminServiceOnPolicyAddLocked(@NonNull EnforcingAdmin enforcingAdmin) {
        int userId = enforcingAdmin.getUserId();

        // A connection is established with DPCs as soon as they are provisioned, so no need to
        // connect when a policy is set.
        if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
            return;
        }
        if (mEnforcingAdmins.contains(userId)
                && mEnforcingAdmins.get(userId).contains(enforcingAdmin)) {
            return;
        }

        if (!mEnforcingAdmins.contains(enforcingAdmin.getUserId())) {
            mEnforcingAdmins.put(enforcingAdmin.getUserId(), new HashSet<>());
        }
        mEnforcingAdmins.get(enforcingAdmin.getUserId()).add(enforcingAdmin);

        mDeviceAdminServiceController.startServiceForAdmin(
                enforcingAdmin.getPackageName(),
                userId,
                /* actionForLog= */ "policy-added");
    }

    /**
     * Called after an admin policy has been removed to stop binding to the admin if they no longer
     * have any policies set.
     */
    private void updateDeviceAdminServiceOnPolicyRemoveLocked(
            @NonNull EnforcingAdmin enforcingAdmin) {
        // TODO(b/263364434): centralise handling in one place.
        // DPCs rely on a constant connection being established as soon as they are provisioned,
        // so we shouldn't disconnect it even if they no longer have policies set.
        if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
            return;
        }
        if (doesAdminHavePolicies(enforcingAdmin)) {
            return;
        }

        int userId = enforcingAdmin.getUserId();

        if (mEnforcingAdmins.contains(userId)) {
            mEnforcingAdmins.get(userId).remove(enforcingAdmin);
            if (mEnforcingAdmins.get(userId).isEmpty()) {
                mEnforcingAdmins.remove(enforcingAdmin.getUserId());
            }
        }

        mDeviceAdminServiceController.stopServiceForAdmin(
                enforcingAdmin.getPackageName(),
                userId,
                /* actionForLog= */ "policy-removed");
    }

    private boolean doesAdminHavePolicies(@NonNull EnforcingAdmin enforcingAdmin) {
        for (String policy : mGlobalPolicies.keySet()) {
            PolicyState<?> policyState = mGlobalPolicies.get(policy);
            if (policyState.getPoliciesSetByAdmins().containsKey(enforcingAdmin)) {
                return true;
            }
        }
        for (int i = 0; i < mLocalPolicies.size(); i++) {
            for (String policy : mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet()) {
                PolicyState<?> policyState = mLocalPolicies.get(
                        mLocalPolicies.keyAt(i)).get(policy);
                if (policyState.getPoliciesSetByAdmins().containsKey(enforcingAdmin)) {
                    return true;
                }
            }
        }
        return false;
    }

    @NonNull
    private Set<EnforcingAdmin> getEnforcingAdminsForUser(int userId) {
        return mEnforcingAdmins.contains(userId)
                ? mEnforcingAdmins.get(userId) : Collections.emptySet();
    }

    private void write() {
    private void write() {
        Log.d(TAG, "Writing device policies to file.");
        Log.d(TAG, "Writing device policies to file.");
        new DevicePoliciesReaderWriter().writeToFileLocked();
        new DevicePoliciesReaderWriter().writeToFileLocked();
@@ -649,6 +830,7 @@ final class DevicePolicyEngine {
        private static final String TAG_LOCAL_POLICY_ENTRY = "local-policy-entry";
        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_GLOBAL_POLICY_ENTRY = "global-policy-entry";
        private static final String TAG_ADMINS_POLICY_ENTRY = "admins-policy-entry";
        private static final String TAG_ADMINS_POLICY_ENTRY = "admins-policy-entry";
        private static final String TAG_ENFORCING_ADMINS_ENTRY = "enforcing-admins-entry";
        private static final String ATTR_USER_ID = "user-id";
        private static final String ATTR_USER_ID = "user-id";
        private static final String ATTR_POLICY_ID = "policy-id";
        private static final String ATTR_POLICY_ID = "policy-id";


@@ -691,6 +873,7 @@ final class DevicePolicyEngine {
        void writeInner(TypedXmlSerializer serializer) throws IOException {
        void writeInner(TypedXmlSerializer serializer) throws IOException {
            writeLocalPoliciesInner(serializer);
            writeLocalPoliciesInner(serializer);
            writeGlobalPoliciesInner(serializer);
            writeGlobalPoliciesInner(serializer);
            writeEnforcingAdminsInner(serializer);
        }
        }


        private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
        private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
@@ -731,6 +914,19 @@ final class DevicePolicyEngine {
            }
            }
        }
        }


        private void writeEnforcingAdminsInner(TypedXmlSerializer serializer) throws IOException {
            if (mEnforcingAdmins != null) {
                for (int i = 0; i < mEnforcingAdmins.size(); i++) {
                    int userId = mEnforcingAdmins.keyAt(i);
                    for (EnforcingAdmin admin : mEnforcingAdmins.get(userId)) {
                        serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMINS_ENTRY);
                        admin.saveToXml(serializer);
                        serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMINS_ENTRY);
                    }
                }
            }
        }

        void readFromFileLocked() {
        void readFromFileLocked() {
            if (!mFile.exists()) {
            if (!mFile.exists()) {
                Log.d(TAG, "" + mFile + " doesn't exist");
                Log.d(TAG, "" + mFile + " doesn't exist");
@@ -765,6 +961,9 @@ final class DevicePolicyEngine {
                    case TAG_GLOBAL_POLICY_ENTRY:
                    case TAG_GLOBAL_POLICY_ENTRY:
                        readGlobalPoliciesInner(parser);
                        readGlobalPoliciesInner(parser);
                        break;
                        break;
                    case TAG_ENFORCING_ADMINS_ENTRY:
                        readEnforcingAdminsInner(parser);
                        break;
                    default:
                    default:
                        Log.e(TAG, "Unknown tag " + tag);
                        Log.e(TAG, "Unknown tag " + tag);
                }
                }
@@ -800,6 +999,15 @@ final class DevicePolicyEngine {
            }
            }
        }
        }


        private void readEnforcingAdminsInner(TypedXmlPullParser parser)
                throws XmlPullParserException {
            EnforcingAdmin admin = EnforcingAdmin.readFromXml(parser);
            if (!mEnforcingAdmins.contains(admin.getUserId())) {
                mEnforcingAdmins.put(admin.getUserId(), new HashSet<>());
            }
            mEnforcingAdmins.get(admin.getUserId()).add(admin);
        }

        @Nullable
        @Nullable
        private PolicyState<?> parseAdminsPolicy(TypedXmlPullParser parser)
        private PolicyState<?> parseAdminsPolicy(TypedXmlPullParser parser)
                throws XmlPullParserException, IOException {
                throws XmlPullParserException, IOException {
+36 −18

File changed.

Preview size limit exceeded, changes collapsed.

Loading