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

Commit 33fc19ce authored by Calin Juravle's avatar Calin Juravle
Browse files

Remove support for PackageDexUsage version 1

PackageVersion version 1 was indroduce in Nougat. Since then,
all devices should have updated to version 2 which was introduced in O.

This simplifies the logic in preparation for adding support for
system server jars.

Test: atest DexManagerTests PackageDexUsageTests
Bug: 148774920
Change-Id: Ib546ed12c5341d01fb12545cd98598b4cf2c5051
parent c6f4f747
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -401,7 +401,8 @@ public class PackageDexOptimizer {
            return DEX_OPT_FAILED;
        }
        String classLoaderContext = null;
        if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
        if (dexUseInfo.isUnsupportedClassLoaderContext()
                || dexUseInfo.isVariableClassLoaderContext()) {
            // If we have an unknown (not yet set), or a variable class loader chain. Just extract
            // the dex file.
            compilerFilter = "extract";
+1 −1
Original line number Diff line number Diff line
@@ -563,7 +563,7 @@ public class DexManager {
            boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
                    dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false,
                    searchResult.mOwningPackageName,
                    PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
                    PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
            update |= newUpdate;
        }
        if (update) {
+46 −115
Original line number Diff line number Diff line
@@ -51,25 +51,21 @@ import java.util.Set;
 * Stat file which store usage information about dex files.
 */
public class PackageDexUsage extends AbstractStatsBase<Void> {
    private final static String TAG = "PackageDexUsage";
    private static final String TAG = "PackageDexUsage";

    // We support previous version to ensure that the usage list remains valid cross OTAs.
    private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 = 1;
    // Version 2 added:
    //  - the list of packages that load the dex files
    //  - class loader contexts for secondary dex files
    //  - usage for all code paths (including splits)
    private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2 = 2;
    // We are currently at version 2.
    // Version 1 was introduced in Nougat and Version 2 in Oreo.
    // We dropped version 1 support in R since all devices should have updated
    // already.
    private static final int PACKAGE_DEX_USAGE_VERSION = 2;

    private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;

    private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
    private static final String PACKAGE_DEX_USAGE_VERSION_HEADER =
            "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";

    private final static String SPLIT_CHAR = ",";
    private final static String CODE_PATH_LINE_CHAR = "+";
    private final static String DEX_LINE_CHAR = "#";
    private final static String LOADING_PACKAGE_CHAR = "@";
    private static final String SPLIT_CHAR = ",";
    private static final String CODE_PATH_LINE_CHAR = "+";
    private static final String DEX_LINE_CHAR = "#";
    private static final String LOADING_PACKAGE_CHAR = "@";

    // One of the things we record about dex files is the class loader context that was used to
    // load them. That should be stable but if it changes we don't keep track of variable contexts.
@@ -77,10 +73,6 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
    // skip optimizations on that dex files.
    /*package*/ static final String VARIABLE_CLASS_LOADER_CONTEXT =
            "=VariableClassLoaderContext=";
    // The markers used for unknown class loader contexts. This can happen if the dex file was
    // recorded in a previous version and we didn't have a chance to update its usage.
    /*package*/ static final String UNKNOWN_CLASS_LOADER_CONTEXT =
            "=UnknownClassLoaderContext=";

    // The marker used for unsupported class loader contexts (no longer written, may occur in old
    // files so discarded on read). Note: this matches
@@ -339,7 +331,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
            version = Integer.parseInt(
                    versionLine.substring(PACKAGE_DEX_USAGE_VERSION_HEADER.length()));
            if (!isSupportedVersion(version)) {
                throw new IllegalStateException("Unexpected version: " + version);
                Slog.w(TAG, "Unexpected package-dex-use version: " + version
                        + ". Not reading from it");
                return;
            }
        }

@@ -377,9 +371,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                    throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
                }

                // In version 2 we added the loading packages and class loader context.
                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
                String classLoaderContext = maybeReadClassLoaderContext(in, version);
                Set<String> loadingPackages = readLoadingPackages(in, version);
                String classLoaderContext = readClassLoaderContext(in, version);

                if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(classLoaderContext)) {
                    // We used to record use of unsupported class loaders, but we no longer do.
@@ -410,34 +403,16 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                }
                currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
            } else if (line.startsWith(CODE_PATH_LINE_CHAR)) {
                // This is a code path used by other apps line.
                if (version < PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
                    throw new IllegalArgumentException("Unexpected code path line when parsing " +
                            "PackageDexUseData: " + line);
                }

                // Expects 2 lines:
                //    +code_paths
                //    @loading_packages
                String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
                Set<String> loadingPackages = readLoadingPackages(in, version);
                currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
            } else {
                // This is a package line.
                if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
                currentPackage = line;
                currentPackageData = new PackageUseInfo();
                } else {
                    // Old version (<2)
                    // We expect it to be: `packageName,isUsedByOtherApps`.
                    String[] elems = line.split(SPLIT_CHAR);
                    if (elems.length != 2) {
                        throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
                    }
                    currentPackage = elems[0];
                    currentPackageData = new PackageUseInfo();
                    currentPackageData.mUsedByOtherAppsBeforeUpgrade = readBoolean(elems[1]);
                }
                data.put(currentPackage, currentPackageData);
            }
        }
@@ -449,30 +424,21 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
    }

    /**
     * Reads the class loader context encoding from the buffer {@code in} if
     * {@code version} is at least {PACKAGE_DEX_USAGE_VERSION}.
     * Reads the class loader context encoding from the buffer {@code in}.
     */
    private String maybeReadClassLoaderContext(BufferedReader in, int version) throws IOException {
        String context = null;
        if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
            context = in.readLine();
    private String readClassLoaderContext(BufferedReader in, int version) throws IOException {
        String context = in.readLine();
        if (context == null) {
            throw new IllegalStateException("Could not find the classLoaderContext line.");
        }
        }
        // The context might be empty if we didn't have the chance to update it after a version
        // upgrade. In this case return the special marker so that we recognize this is an unknown
        // context.
        return context == null ? UNKNOWN_CLASS_LOADER_CONTEXT : context;
        return context;
    }

    /**
     * Reads the list of loading packages from the buffer {@code in} if
     * {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}.
     * Reads the list of loading packages from the buffer {@code in}.
     */
    private Set<String> maybeReadLoadingPackages(BufferedReader in, int version)
    private Set<String> readLoadingPackages(BufferedReader in, int version)
            throws IOException {
        if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
        String line = in.readLine();
        if (line == null) {
            throw new IllegalStateException("Could not find the loadingPackages line.");
@@ -486,9 +452,6 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                    line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
            return result;
        }
        } else {
            return Collections.emptySet();
        }
    }

    /**
@@ -501,8 +464,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
    }

    private boolean isSupportedVersion(int version) {
        return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1
                || version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
        return version == PACKAGE_DEX_USAGE_VERSION;
    }

    /**
@@ -544,14 +506,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                        }
                    }

                    // In case the package was marked as used by other apps in a previous version
                    // propagate the flag to all the code paths.
                    // See mUsedByOtherAppsBeforeUpgrade docs on why it is important to do it.
                    if (packageUseInfo.mUsedByOtherAppsBeforeUpgrade) {
                        for (String codePath : codePaths) {
                            packageUseInfo.mergeCodePathUsedByOtherApps(codePath, true, null, null);
                        }
                    } else if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
                    if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
                        && packageUseInfo.mDexUseInfoMap.isEmpty()) {
                        // The package is not used by other apps and we removed all its dex files
                        // records. Remove the entire package record as well.
@@ -718,19 +673,6 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
        // Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
        private final Map<String, DexUseInfo> mDexUseInfoMap;

        // Keeps track of whether or not this package was used by other apps before
        // we upgraded to VERSION 4 which records the info for each code path separately.
        // This is unwanted complexity but without it we risk to profile guide compile
        // something that supposed to be shared. For example:
        //   1) we determine that chrome is used by another app
        //   2) we take an OTA which upgrades the way we keep track of usage data
        //   3) chrome doesn't get used until the background job executes
        //   4) as part of the backgound job we now think that chrome is not used by others
        //      and we speed-profile.
        //   5) as a result the next time someone uses chrome it will extract from apk since
        //      the compiled code will be private.
        private boolean mUsedByOtherAppsBeforeUpgrade;

        /*package*/ PackageUseInfo() {
            mCodePathsUsedByOtherApps = new HashMap<>();
            mDexUseInfoMap = new HashMap<>();
@@ -790,10 +732,6 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
         * Returns whether or not there was an update.
         */
        /*package*/ boolean clearCodePathUsedByOtherApps() {
            // Update mUsedByOtherAppsBeforeUpgrade as well to be consistent with
            // the new data. This is not saved to disk so we don't need to return it.
            mUsedByOtherAppsBeforeUpgrade = true;

            if (mCodePathsUsedByOtherApps.isEmpty()) {
                return false;
            } else {
@@ -847,11 +785,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
            boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);

            String oldClassLoaderContext = mClassLoaderContext;
            if (isUnknownOrUnsupportedContext(mClassLoaderContext)) {
                // Can happen if we read a previous version.
            if (isUnsupportedContext(mClassLoaderContext)) {
                mClassLoaderContext = dexUseInfo.mClassLoaderContext;
            } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext)
                        && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
            } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
                // We detected a context change.
                mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
            }
@@ -862,11 +798,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                    || !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
        }

        private static boolean isUnknownOrUnsupportedContext(String context) {
            // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases
            // into UNSUPPORTED_CLASS_LOADER_CONTEXT.
            return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context)
                    || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
        private static boolean isUnsupportedContext(String context) {
            return UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
        }

        public boolean isUsedByOtherApps() {
@@ -887,10 +820,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {

        public String getClassLoaderContext() { return mClassLoaderContext; }

        public boolean isUnknownClassLoaderContext() {
            // The class loader context may be unknown if we loaded the data from a previous version
            // which didn't save the context.
            return isUnknownOrUnsupportedContext(mClassLoaderContext);
        public boolean isUnsupportedClassLoaderContext() {
            return isUnsupportedContext(mClassLoaderContext);
        }

        public boolean isVariableClassLoaderContext() {
+2 −102
Original line number Diff line number Diff line
@@ -420,45 +420,19 @@ public class PackageDexUsageTests {
        assertPackageDexUsage(null, expectedContext);
    }

    @Test
    public void testRecordClassLoaderContextTransitionFromUnknown() {
        // Record a secondary dex file.
        TestData unknownContext = mFooSecondary1User0.updateClassLoaderContext(
                PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
        assertTrue(record(unknownContext));

        assertPackageDexUsage(null, unknownContext);
        writeAndReadBack();
        assertPackageDexUsage(null, unknownContext);

        // Now update the secondary dex record with a class loader context. This simulates the
        // version 2 to version 3 upgrade.

        assertTrue(record(mFooSecondary1User0));

        assertPackageDexUsage(null, mFooSecondary1User0);
        writeAndReadBack();
        assertPackageDexUsage(null, mFooSecondary1User0);
    }

    @Test
    public void testDexUsageClassLoaderContext() {
        final boolean isUsedByOtherApps = false;
        final int userId = 0;
        PackageDexUsage.DexUseInfo validContext = new DexUseInfo(isUsedByOtherApps, userId,
                "valid_context", "arm");
        assertFalse(validContext.isUnknownClassLoaderContext());
        assertFalse(validContext.isUnsupportedClassLoaderContext());
        assertFalse(validContext.isVariableClassLoaderContext());

        PackageDexUsage.DexUseInfo variableContext = new DexUseInfo(isUsedByOtherApps, userId,
                PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, "arm");
        assertFalse(variableContext.isUnknownClassLoaderContext());
        assertFalse(variableContext.isUnsupportedClassLoaderContext());
        assertTrue(variableContext.isVariableClassLoaderContext());

        PackageDexUsage.DexUseInfo unknownContext = new DexUseInfo(isUsedByOtherApps, userId,
                PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT, "arm");
        assertTrue(unknownContext.isUnknownClassLoaderContext());
        assertFalse(unknownContext.isVariableClassLoaderContext());
    }

    @Test
@@ -482,80 +456,6 @@ public class PackageDexUsageTests {
        assertPackageDexUsage(mBarBaseUser0);
    }

    @Test
    public void testReadVersion1() {
        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
        // Equivalent to
        //   record(mFooSplit2UsedByOtherApps0);
        //   record(mFooSecondary1User0);
        //   record(mFooSecondary2UsedByOtherApps0);
        //   record(mBarBaseUser0);
        //   record(mBarSecondary1User0);
        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__1\n"
                + "com.google.foo,1\n"
                + "#/data/user/0/com.google.foo/sec-1.dex\n"
                + "0,0," + isa + "\n"
                + "#/data/user/0/com.google.foo/sec-2.dex\n"
                + "0,1," + isa + "\n"
                + "com.google.bar,0\n"
                + "#/data/user/0/com.google.bar/sec-1.dex\n"
                + "0,0," + isa + "\n";

        PackageDexUsage packageDexUsage = new PackageDexUsage();
        try {
            packageDexUsage.read(new StringReader(content));
        } catch (IOException e) {
            fail();
        }

        // After the read we must sync the data to fill the missing information on the code paths.
        Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
        Map<String, Set<String>> packageToCodePaths = new HashMap<>();

        // Handle foo package.
        packageToUsersMap.put(mFooSplit2UsedByOtherApps0.mPackageName,
            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
        packageToCodePaths.put(mFooSplit2UsedByOtherApps0.mPackageName,
            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
                mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
        // Handle bar package.
        packageToUsersMap.put(mBarBaseUser0.mPackageName,
            new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
        packageToCodePaths.put(mBarBaseUser0.mPackageName,
            new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));

        // Sync the data.
        packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);

        // Update the class loaders to unknown before asserting if needed. Before version 2 we
        // didn't have any.
        String unknown = PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT;
        TestData fooBaseUser0 = mFooBaseUser0.updateClassLoaderContext(unknown);
        TestData fooSplit1User0 = mFooSplit1User0.updateClassLoaderContext(unknown);
        TestData fooSplit2UsedByOtherApps0 =
            mFooSplit2UsedByOtherApps0.updateClassLoaderContext(unknown);
        TestData fooSecondary1User0 = mFooSecondary1User0.updateClassLoaderContext(unknown);
        TestData fooSecondary2UsedByOtherApps0 =
            mFooSecondary2UsedByOtherApps0.updateClassLoaderContext(unknown);
        TestData barBaseUser0 = mBarBaseUser0.updateClassLoaderContext(unknown);
        TestData barSecondary1User0 = mBarSecondary1User0.updateClassLoaderContext(unknown);

        // Assert foo code paths. Note that we ignore the users during upgrade.
        final Set<String> ignoredUsers = null;
        assertPackageDexUsage(packageDexUsage, ignoredUsers,
            fooSplit2UsedByOtherApps0, fooSecondary1User0, fooSecondary2UsedByOtherApps0);
        // Because fooSplit2UsedByOtherApps0 is used by others, all the other code paths must
        // share the same data.
        assertPackageDexUsage(packageDexUsage, ignoredUsers,
            fooSplit1User0.updateUseByOthers(true),
            fooSecondary1User0, fooSecondary2UsedByOtherApps0);
        assertPackageDexUsage(packageDexUsage, ignoredUsers, fooBaseUser0.updateUseByOthers(true),
            fooSecondary1User0, fooSecondary2UsedByOtherApps0);

        // Assert bar code paths. Note that we ignore the users during upgrade.
        assertPackageDexUsage(packageDexUsage, ignoredUsers, barBaseUser0, barSecondary1User0);
    }

    private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
        assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
    }