Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c8fd297 authored by Calin Juravle's avatar Calin Juravle
Browse files

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
parent 3f2b728b
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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. "
+27 −5
Original line number Diff line number Diff line
@@ -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();
                                }
                            }
                        }
                    }

+39 −4
Original line number Diff line number Diff line
@@ -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() {