Merge "Store admin suspended packages in ActiveAdmin"

This commit is contained in:
Pavel Grafov
2022-12-11 14:22:14 +00:00
committed by Android (Google) Code Review
5 changed files with 186 additions and 4 deletions

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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(