diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 9f2ec47335f3e..e8765ad973f37 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -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 packagesToKeepDataAbout = new ArrayList<>(); + mPackageDexUsage.syncData( + packageToUsersMap, packageToCodePaths, packagesToKeepDataAbout); } catch (Exception e) { mPackageDexUsage.clear(); Slog.w(TAG, "Exception while loading package dex usage. " diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java index 1a3c18f61333d..7ac09e373d20a 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java +++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java @@ -474,13 +474,19 @@ public class PackageDexUsage extends AbstractStatsBase { * Syncs the existing data with the set of available packages by removing obsolete entries. */ /*package*/ void syncData(Map> packageToUsersMap, - Map> packageToCodePaths) { + Map> packageToCodePaths, + List packagesToKeepDataAbout) { synchronized (mPackageUseInfoMap) { Iterator> pIt = mPackageUseInfoMap.entrySet().iterator(); while (pIt.hasNext()) { Map.Entry 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 users = packageToUsersMap.get(packageName); if (users == null) { @@ -501,11 +507,27 @@ public class PackageDexUsage extends AbstractStatsBase { // Sync the code paths. Set codePaths = packageToCodePaths.get(packageName); - Iterator>> codeIt = + + Iterator>> recordedIt = packageUseInfo.mPrimaryCodePaths.entrySet().iterator(); - while (codeIt.hasNext()) { - if (!codePaths.contains(codeIt.next().getKey())) { - codeIt.remove(); + while (recordedIt.hasNext()) { + Map.Entry> 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 recordedLoadingPackages = entry.getValue(); + Iterator recordedLoadingPackagesIt = + recordedLoadingPackages.iterator(); + while (recordedLoadingPackagesIt.hasNext()) { + String recordedLoadingPackage = recordedLoadingPackagesIt.next(); + if (!packagesToKeepDataAbout.contains(recordedLoadingPackage) + && !packageToUsersMap.containsKey(recordedLoadingPackage)) { + recordedLoadingPackagesIt.remove(); + } + } } } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java index 08fc87758f57d..2829fa7bf83f7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java @@ -282,13 +282,48 @@ public class PackageDexUsageTests { Map> packageToCodePaths = new HashMap<>(); packageToCodePaths.put(mBarBaseUser0.mPackageName, new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile))); - mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths); + mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList()); // 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 fooUsers = new HashSet<>(Arrays.asList( + new String[] {mFooBaseUser0.mPackageName})); + assertTrue(record(packageDexUsage, mFooBaseUser0, fooUsers)); + // Write a record that would be kept by default. + Set 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> packageToUsersMap = new HashMap<>(); + packageToUsersMap.put(mBarBaseUser0.mPackageName, + new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId))); + Map> packageToCodePaths = new HashMap<>(); + packageToCodePaths.put(mBarBaseUser0.mPackageName, + new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile))); + + // Sync data. + List keepData = new ArrayList(); + 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 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() {