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

Commit 9a512e9c authored by Pavel Grafov's avatar Pavel Grafov Committed by Android (Google) Code Review
Browse files

Merge "Store admin suspended packages in ActiveAdmin"

parents 7b1d53e3 3532c6ac
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -162,6 +162,7 @@ class ActiveAdmin {
    private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIG =
            "preferential_network_service_config";
    private static final String TAG_PROTECTED_PACKAGES = "protected_packages";
    private static final String TAG_SUSPENDED_PACKAGES = "suspended-packages";
    private static final String ATTR_VALUE = "value";
    private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
    private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -257,6 +258,8 @@ class ActiveAdmin {
    // List of packages for which the user cannot invoke "clear data" or "force stop".
    List<String> protectedPackages;

    List<String> suspendedPackages;

    // Wi-Fi SSID restriction policy.
    WifiSsidPolicy mWifiSsidPolicy;

@@ -508,6 +511,7 @@ class ActiveAdmin {
        writePackageListToXml(out, TAG_KEEP_UNINSTALLED_PACKAGES, keepUninstalledPackages);
        writePackageListToXml(out, TAG_METERED_DATA_DISABLED_PACKAGES, meteredDisabledPackages);
        writePackageListToXml(out, TAG_PROTECTED_PACKAGES, protectedPackages);
        writePackageListToXml(out, TAG_SUSPENDED_PACKAGES, suspendedPackages);
        if (hasUserRestrictions()) {
            UserRestrictionsUtils.writeRestrictions(
                    out, userRestrictions, TAG_USER_RESTRICTIONS);
@@ -776,6 +780,8 @@ class ActiveAdmin {
                meteredDisabledPackages = readPackageList(parser, tag);
            } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
                protectedPackages = readPackageList(parser, tag);
            } else if (TAG_SUSPENDED_PACKAGES.equals(tag)) {
                suspendedPackages = readPackageList(parser, tag);
            } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
                userRestrictions = UserRestrictionsUtils.readRestrictions(parser);
            } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) {
@@ -1225,6 +1231,11 @@ class ActiveAdmin {
            pw.println(protectedPackages);
        }

        if (suspendedPackages != null) {
            pw.print("suspendedPackages=");
            pw.println(suspendedPackages);
        }

        pw.print("organizationColor=");
        pw.println(organizationColor);

+44 −3
Original line number Diff line number Diff line
@@ -545,7 +545,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    // to decide whether an existing policy in the {@link #DEVICE_POLICIES_XML} needs to
    // be upgraded. See {@link PolicyVersionUpgrader} on instructions how to add an upgrade
    // step.
    static final int DPMS_VERSION = 3;
    static final int DPMS_VERSION = 4;
    static {
        SECURE_SETTINGS_ALLOWLIST = new ArraySet<>();
@@ -1500,8 +1500,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        }
        PackageManager getPackageManager(int userId) {
            return mContext
                    .createContextAsUser(UserHandle.of(userId), 0 /* flags */).getPackageManager();
            try {
                return createContextAsUser(UserHandle.of(userId)).getPackageManager();
            } catch (NameNotFoundException e) {
                throw new IllegalStateException(e);
            }
        }
        PowerManagerInternal getPowerManagerInternal() {
@@ -3114,6 +3117,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            List<UserInfo> allUsers = mUserManager.getUsers();
            return allUsers.stream().mapToInt(u -> u.id).toArray();
        }
        @Override
        public List<String> getPlatformSuspendedPackages(int userId) {
            PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
            return mInjector.getPackageManager(userId)
                    .getInstalledPackages(PackageManager.PackageInfoFlags.of(
                            MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE))
                    .stream()
                    .map(packageInfo -> packageInfo.packageName)
                    .filter(pkg ->
                            PLATFORM_PACKAGE_NAME.equals(pmi.getSuspendingPackage(pkg, userId))
                    )
                    .collect(Collectors.toList());
        }
    }
    private void performPolicyVersionUpgrade() {
@@ -11386,6 +11403,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            Slogf.w(LOG_TAG, "PM failed to suspend packages (%s)", Arrays.toString(packageNames));
            return packageNames;
        }
        ArraySet<String> changed = new ArraySet<>(packageNames);
        if (suspended) {
            // Only save those packages that are actually suspended. If a package is exempt or is
            // unsuspendable, it is skipped.
            changed.removeAll(List.of(nonSuspendedPackages));
        } else {
            // If an admin tries to unsuspend a package that is either exempt or is not
            // suspendable, drop it from the stored list assuming it must be already unsuspended.
            changed.addAll(exemptApps);
        }
        synchronized (getLockObject()) {
            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
            ArraySet<String> current = new ArraySet<>(admin.suspendedPackages);
            if (suspended) {
                current.addAll(changed);
            } else {
                current.removeAll(changed);
            }
            admin.suspendedPackages = current.isEmpty() ? null : new ArrayList<>(current);
            saveSettingsLocked(caller.getUserId());
        }
        if (exemptApps.isEmpty()) {
            return nonSuspendedPackages;
        }
+6 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.ComponentName;

import com.android.internal.util.JournaledFile;

import java.util.List;
import java.util.function.Function;

/**
@@ -48,4 +49,9 @@ public interface PolicyUpgraderDataProvider {
     * Returns the users to upgrade.
     */
    int[] getUsersForUpgrade();

    /**
     * Returns packages suspended by platform for a given user.
     */
    List<String> getPlatformSuspendedPackages(int userId);
}
+44 −0
Original line number Diff line number Diff line
@@ -104,6 +104,12 @@ public class PolicyVersionUpgrader {
            currentVersion = 3;
        }

        if (currentVersion == 3) {
            Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
            upgradePackageSuspension(allUsers, ownersData, allUsersData);
            currentVersion = 4;
        }

        writePoliciesAndVersion(allUsers, allUsersData, ownersData, currentVersion);
    }

@@ -170,6 +176,44 @@ public class PolicyVersionUpgrader {
        }
    }

    /**
     * This upgrade step stores packages suspended via DPM.setPackagesSuspended() into ActiveAdmin
     * data structure. Prior to this it was only persisted in PackageManager which doesn't have any
     * way of knowing which admin suspended it.
     */
    private void upgradePackageSuspension(
            int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
        if (ownersData.mDeviceOwner != null) {
            saveSuspendedPackages(allUsersData, ownersData.mDeviceOwnerUserId,
                    ownersData.mDeviceOwner.admin);
        }

        for (int i = 0; i < ownersData.mProfileOwners.size(); i++) {
            int ownerUserId = ownersData.mProfileOwners.keyAt(i);
            OwnersData.OwnerInfo ownerInfo = ownersData.mProfileOwners.valueAt(i);
            saveSuspendedPackages(allUsersData, ownerUserId, ownerInfo.admin);
        }
    }

    private void saveSuspendedPackages(SparseArray<DevicePolicyData> allUsersData, int ownerUserId,
            ComponentName ownerPackage) {
        DevicePolicyData ownerUserData = allUsersData.get(ownerUserId);
        if (ownerUserData == null) {
            Slog.e(LOG_TAG, "No policy data for owner user, cannot migrate suspended packages");
            return;
        }

        ActiveAdmin ownerAdmin = ownerUserData.mAdminMap.get(ownerPackage);
        if (ownerAdmin == null) {
            Slog.e(LOG_TAG, "No admin for owner, cannot migrate suspended packages");
            return;
        }

        ownerAdmin.suspendedPackages = mProvider.getPlatformSuspendedPackages(ownerUserId);
        Slog.i(LOG_TAG, String.format("Saved %d packages suspended by %s in user %d",
                ownerAdmin.suspendedPackages.size(), ownerPackage, ownerUserId));
    }

    private OwnersData loadOwners(int[] allUsers) {
        OwnersData ownersData = new OwnersData(mPathProvider);
        ownersData.load(allUsers);
+81 −1
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.content.pm.ApplicationInfo;
import android.os.IpcDataCache;
import android.os.Parcel;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Xml;

import androidx.test.InstrumentationRegistry;
@@ -47,29 +48,42 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import javax.xml.parsers.DocumentBuilderFactory;

@RunWith(JUnit4.class)
public class PolicyVersionUpgraderTest extends DpmTestBase {
    // NOTE: Only change this value if the corresponding CL also adds a test to test the upgrade
    // to the new version.
    private static final int LATEST_TESTED_VERSION = 3;
    private static final int LATEST_TESTED_VERSION = 4;
    public static final String PERMISSIONS_TAG = "admin-can-grant-sensors-permissions";
    public static final String DEVICE_OWNER_XML = "device_owner_2.xml";
    private ComponentName mFakeAdmin;

    private class FakePolicyUpgraderDataProvider implements PolicyUpgraderDataProvider {
        Map<ComponentName, DeviceAdminInfo> mComponentToDeviceAdminInfo = new HashMap<>();
        ArrayList<String> mPlatformSuspendedPackages = new ArrayList<>();
        int[] mUsers;

        private JournaledFile makeJournaledFile(int userId, String fileName) {
@@ -98,6 +112,11 @@ public class PolicyVersionUpgraderTest extends DpmTestBase {
        public int[] getUsersForUpgrade() {
            return mUsers;
        }

        @Override
        public List<String> getPlatformSuspendedPackages(int userId) {
            return mPlatformSuspendedPackages;
        }
    }

    private final Context mRealTestContext = InstrumentationRegistry.getTargetContext();
@@ -256,11 +275,72 @@ public class PolicyVersionUpgraderTest extends DpmTestBase {
        assertThat(isTagPresent(readPoliciesFileToStream(ownerUser), newTag)).isTrue();
    }

    @Test
    public void testAdminPackageSuspensionSaved() throws Exception {
        final int ownerUser = 0;
        mProvider.mUsers = new int[]{ownerUser};
        getServices().addUser(ownerUser, FLAG_PRIMARY, USER_TYPE_FULL_SYSTEM);
        setUpPackageManagerForAdmin(admin1, UserHandle.getUid(ownerUser, 123 /* admin app ID */));
        writeVersionToXml(3);
        preparePoliciesFile(ownerUser, "device_policies.xml");
        prepareDeviceOwnerFile(ownerUser, "device_owner_2.xml");

        // Pretend package manager thinks these packages are suspended by the platform.
        Set<String> suspendedPkgs = Set.of("com.some.app", "foo.bar.baz");
        mProvider.mPlatformSuspendedPackages.addAll(suspendedPkgs);

        mUpgrader.upgradePolicy(4);

        assertThat(readVersionFromXml()).isAtLeast(4);

        assertAdminSuspendedPackages(ownerUser, suspendedPkgs);
    }

    private void assertAdminSuspendedPackages(int ownerUser, Set<String> suspendedPkgs)
            throws Exception {
        Document policies = readPolicies(ownerUser);
        Element adminElem =
                (Element) policies.getDocumentElement().getElementsByTagName("admin").item(0);
        Element suspendedElem =
                (Element) adminElem.getElementsByTagName("suspended-packages").item(0);
        NodeList pkgsNodes = suspendedElem.getElementsByTagName("item");
        Set<String> storedSuspendedPkgs = new ArraySet<>();
        for (int i = 0; i < pkgsNodes.getLength(); i++) {
            Element item = (Element) pkgsNodes.item(i);
            storedSuspendedPkgs.add(item.getAttribute("value"));
        }
        assertThat(storedSuspendedPkgs).isEqualTo(suspendedPkgs);
    }

    @Test
    public void isLatestVersionTested() {
        assertThat(DevicePolicyManagerService.DPMS_VERSION).isEqualTo(LATEST_TESTED_VERSION);
    }

    /**
     * Reads ABX binary XML, converts it to text, and returns as an input stream.
     */
    private InputStream abxToXmlStream(File file) throws Exception {
        FileInputStream fileIn = new FileInputStream(file);
        XmlPullParser in = Xml.newBinaryPullParser();
        in.setInput(fileIn, StandardCharsets.UTF_8.name());

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        XmlSerializer out = Xml.newSerializer();
        out.setOutput(byteOut, StandardCharsets.UTF_8.name());

        Xml.copy(in, out);
        out.flush();

        return new ByteArrayInputStream(byteOut.toByteArray());
    }

    private Document readPolicies(int userId) throws Exception {
        File policiesFile = mProvider.makeDevicePoliciesJournaledFile(userId).chooseForRead();
        InputStream is = abxToXmlStream(policiesFile);
        return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
    }

    private void writeVersionToXml(int dpmsVersion) throws IOException {
        JournaledFile versionFile = mProvider.makePoliciesVersionJournaledFile(0);
        Files.asCharSink(versionFile.chooseForWrite(), Charset.defaultCharset()).write(