diff options
| author | 2022-11-21 17:40:52 +0000 | |
|---|---|---|
| committer | 2022-12-09 17:35:33 +0000 | |
| commit | 3532c6accf5e69f35750c399dbf42886bcff43d8 (patch) | |
| tree | d25c3c7887d710c7f9beac997544bfe9c2cf07c7 | |
| parent | a2063a9dca2c6e6888471839d9fb4e5f0acd51ba (diff) | |
Store admin suspended packages in ActiveAdmin
Bug: 258823777
Test: atest PolicyVersionUpgraderTest
Change-Id: Ia41884124418453d63490f5fea637dbb7dada4ac
5 files changed, 186 insertions, 4 deletions
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 8047a534f4a3..9af30ba14b47 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -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); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1ec2438613d2..3d09e69e0b1d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -546,7 +546,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<>(); @@ -1498,8 +1498,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() { @@ -3111,6 +3114,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() { @@ -11383,6 +11400,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; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java index 147474907f29..c0ef0b0f80df 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java @@ -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); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java index 808133164cd1..1fe4b571a770 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java @@ -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); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java index d540734d0938..1779b16a7471 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java @@ -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(); @@ -257,10 +276,71 @@ public class PolicyVersionUpgraderTest extends DpmTestBase { } @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( |