Store admin suspended packages in ActiveAdmin
Bug: 258823777 Test: atest PolicyVersionUpgraderTest Change-Id: Ia41884124418453d63490f5fea637dbb7dada4ac
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user