Allow non-user packages to be kept in the use data list

By default, we clean up the data from the package-dex-use.list at
every boot. The clean up will remove any obsolete or uninstalled
packages.

Since system server will not show up as a regular app, we need
to add a mechanism to ensure that its dex files are not cleaned up.

Test: atest DexManagerTests PackageManagerTests
Bug: 148774920
Change-Id: I0788163b066af5713b8763f1875338add2d49aa9
This commit is contained in:
Calin Juravle
2020-03-31 14:03:01 -07:00
parent 3f2b728bba
commit 7c8fd29787
3 changed files with 70 additions and 10 deletions

View File

@@ -45,6 +45,7 @@ import dalvik.system.VMRuntime;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -353,7 +354,9 @@ public class DexManager {
try {
mPackageDexUsage.read();
mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
List<String> packagesToKeepDataAbout = new ArrayList<>();
mPackageDexUsage.syncData(
packageToUsersMap, packageToCodePaths, packagesToKeepDataAbout);
} catch (Exception e) {
mPackageDexUsage.clear();
Slog.w(TAG, "Exception while loading package dex usage. "

View File

@@ -474,13 +474,19 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
* Syncs the existing data with the set of available packages by removing obsolete entries.
*/
/*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap,
Map<String, Set<String>> packageToCodePaths) {
Map<String, Set<String>> packageToCodePaths,
List<String> packagesToKeepDataAbout) {
synchronized (mPackageUseInfoMap) {
Iterator<Map.Entry<String, PackageUseInfo>> pIt =
mPackageUseInfoMap.entrySet().iterator();
while (pIt.hasNext()) {
Map.Entry<String, PackageUseInfo> pEntry = pIt.next();
String packageName = pEntry.getKey();
if (packagesToKeepDataAbout.contains(packageName)) {
// This is a package for which we should keep the data even if it's not
// in the list of user packages.
continue;
}
PackageUseInfo packageUseInfo = pEntry.getValue();
Set<Integer> users = packageToUsersMap.get(packageName);
if (users == null) {
@@ -501,11 +507,27 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
// Sync the code paths.
Set<String> codePaths = packageToCodePaths.get(packageName);
Iterator<Map.Entry<String, Set<String>>> codeIt =
Iterator<Map.Entry<String, Set<String>>> recordedIt =
packageUseInfo.mPrimaryCodePaths.entrySet().iterator();
while (codeIt.hasNext()) {
if (!codePaths.contains(codeIt.next().getKey())) {
codeIt.remove();
while (recordedIt.hasNext()) {
Map.Entry<String, Set<String>> entry = recordedIt.next();
String recordedCodePath = entry.getKey();
if (!codePaths.contains(recordedCodePath)) {
// Clean up a non existing code path.
recordedIt.remove();
} else {
// Clean up a non existing loading package.
Set<String> recordedLoadingPackages = entry.getValue();
Iterator<String> recordedLoadingPackagesIt =
recordedLoadingPackages.iterator();
while (recordedLoadingPackagesIt.hasNext()) {
String recordedLoadingPackage = recordedLoadingPackagesIt.next();
if (!packagesToKeepDataAbout.contains(recordedLoadingPackage)
&& !packageToUsersMap.containsKey(recordedLoadingPackage)) {
recordedLoadingPackagesIt.remove();
}
}
}
}

View File

@@ -282,13 +282,48 @@ public class PackageDexUsageTests {
Map<String, Set<String>> packageToCodePaths = new HashMap<>();
packageToCodePaths.put(mBarBaseUser0.mPackageName,
new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<String>());
// Assert that only user 1 files are there.
assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
assertNull(mPackageDexUsage.getPackageUseInfo(mFooBaseUser0.mPackageName));
}
@Test
public void testSyncDataKeepPackages() {
PackageDexUsage packageDexUsage = new PackageDexUsage();
// Write the record we want to keep and which won't be keep by default.
Set<String> fooUsers = new HashSet<>(Arrays.asList(
new String[] {mFooBaseUser0.mPackageName}));
assertTrue(record(packageDexUsage, mFooBaseUser0, fooUsers));
// Write a record that would be kept by default.
Set<String> barUsers = new HashSet<>(Arrays.asList(
new String[] {"another.package", mFooBaseUser0.mPackageName}));
assertTrue(record(packageDexUsage, mBarBaseUser0, barUsers));
// Construct the user packages and their code paths (things that will be
// kept by default during sync).
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
packageToUsersMap.put(mBarBaseUser0.mPackageName,
new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
Map<String, Set<String>> packageToCodePaths = new HashMap<>();
packageToCodePaths.put(mBarBaseUser0.mPackageName,
new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
// Sync data.
List<String> keepData = new ArrayList<String>();
keepData.add(mFooBaseUser0.mPackageName);
packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, keepData);
// Assert that both packages are kept
assertPackageDexUsage(packageDexUsage, fooUsers, mFooBaseUser0);
// "another.package" should not be in the loading packages after sync.
Set<String> expectedBarUsers = new HashSet<>(Arrays.asList(
new String[] {mFooBaseUser0.mPackageName}));
assertPackageDexUsage(packageDexUsage, expectedBarUsers,
mBarBaseUser0.updateUsedBy(mFooBaseUser0.mPackageName));
}
@Test
public void testRemovePackage() {
// Record Bar secondaries for two different users.
@@ -501,7 +536,7 @@ public class PackageDexUsageTests {
new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
// Sync the data.
packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<>());
// Assert foo code paths.
assertPackageDexUsage(
@@ -654,9 +689,9 @@ public class PackageDexUsageTests {
mPrimaryOrSplit, mUsedBy, newContext);
}
private TestData updateUseByOthers(boolean newUsedByOthers) {
private TestData updateUsedBy(String newUsedBy) {
return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa,
mPrimaryOrSplit, mUsedBy, mClassLoaderContext);
mPrimaryOrSplit, newUsedBy, mClassLoaderContext);
}
private boolean isUsedByOtherApps() {