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

Commit 653356f1 authored by Nicolas Geoffray's avatar Nicolas Geoffray
Browse files

Support dexopting shared libraries.

Test: adb shell setprop dalvik.vm.boot-image /system/framework/apex.art && see compilations in dalvik-cache
Test: BackgroundDexOptServiceIntegrationTests
Test: DexoptUtilsTest
Bug: 119800099
Change-Id: I3445b8743d576e09c9a339602307ba3a219db1fc
parent c76c5529
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -25,10 +25,12 @@ import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.storage.StorageManager;
import android.util.Log;
import android.util.Slog;

@@ -148,6 +150,51 @@ public class PackageDexOptimizer {
        }
    }

    int performDexOpt(SharedLibraryInfo info, String[] instructionSets, DexoptOptions options) {
        String classLoaderContext = DexoptUtils.getClassLoaderContext(info);
        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
        String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
                PackageManagerService.REASON_SHARED);
        int result = DEX_OPT_SKIPPED;
        for (String instructionSet : dexCodeInstructionSets) {
            int dexoptNeeded = getDexoptNeeded(
                        info.getPath(), instructionSet, compilerFilter,
                        classLoaderContext, false /* newProfile */,
                        false /* downgrade */);
            if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
                continue;
            }
            // Special string recognized by installd.
            final String packageName = "*";
            final String outputPath = null;
            int dexFlags = DEXOPT_PUBLIC
                    | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
                    | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
            dexFlags = adjustDexoptFlags(dexFlags);
            final String uuid = StorageManager.UUID_SYSTEM;
            final String seInfo = null;
            final int targetSdkVersion = 0;  // Builtin libraries targets the system's SDK version
            try {
                mInstaller.dexopt(info.getPath(), Process.SYSTEM_UID, packageName,
                        instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
                        uuid, classLoaderContext, seInfo, false /* downgrade */,
                        targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
                        getReasonName(options.getCompilationReason()));
                // The end result is:
                //  - FAILED if any path failed,
                //  - PERFORMED if at least one path needed compilation,
                //  - SKIPPED when all paths are up to date
                if (result != DEX_OPT_FAILED) {
                    result = DEX_OPT_PERFORMED;
                }
            } catch (InstallerException e) {
                Slog.w(TAG, "Failed to dexopt", e);
                result = DEX_OPT_FAILED;
            }
        }
        return result;
    }

    /**
     * Performs dexopt on all code paths of the given package.
     * It assumes the install lock is held.
+44 −50
Original line number Diff line number Diff line
@@ -9443,18 +9443,27 @@ public class PackageManagerService extends IPackageManager.Stub
        // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
        // and the first package that uses the library will dexopt it. The
        // others will see that the compiled code for the library is up to date.
        Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
        Collection<SharedLibraryInfo> deps = findSharedLibraries(p);
        final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
        if (!deps.isEmpty()) {
            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
                    options.getCompilationReason(), options.getCompilerFilter(),
                    options.getSplitName(),
                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
            for (PackageParser.Package depPackage : deps) {
            for (SharedLibraryInfo info : deps) {
                PackageParser.Package depPackage = null;
                synchronized (mPackages) {
                    depPackage = mPackages.get(info.getPackageName());
                }
                if (depPackage != null) {
                    // TODO: Analyze and investigate if we (should) profile libraries.
                    pdo.performDexOpt(depPackage, instructionSets,
                            getOrCreateCompilerPackageStats(depPackage),
                    mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
                            mDexManager.getPackageUseInfoOrDefault(depPackage.packageName),
                            libraryOptions);
                } else {
                    pdo.performDexOpt(info, instructionSets, libraryOptions);
                }
            }
        }
        return pdo.performDexOpt(p, instructionSets,
@@ -9494,63 +9503,48 @@ public class PackageManagerService extends IPackageManager.Stub
        return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
    }
    List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
        if (p.usesLibraries != null || p.usesOptionalLibraries != null
                || p.usesStaticLibraries != null) {
            ArrayList<PackageParser.Package> retValue = new ArrayList<>();
    private static List<SharedLibraryInfo> findSharedLibraries(PackageParser.Package p) {
        if (p.usesLibraryInfos != null) {
            ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
            Set<String> collectedNames = new HashSet<>();
            findSharedNonSystemLibrariesRecursive(p, retValue, collectedNames);
            retValue.remove(p);
            for (SharedLibraryInfo info : p.usesLibraryInfos) {
                findSharedLibrariesRecursive(info, retValue, collectedNames);
            }
            return retValue;
        } else {
            return Collections.emptyList();
        }
    }
    private void findSharedNonSystemLibrariesRecursive(PackageParser.Package p,
            ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
        if (!collectedNames.contains(p.packageName)) {
            collectedNames.add(p.packageName);
            collected.add(p);
    private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
            ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
        if (!collectedNames.contains(info.getName())) {
            collectedNames.add(info.getName());
            collected.add(info);
            if (p.usesLibraries != null) {
                findSharedNonSystemLibrariesRecursive(p.usesLibraries,
                        null, collected, collectedNames);
            }
            if (p.usesOptionalLibraries != null) {
                findSharedNonSystemLibrariesRecursive(p.usesOptionalLibraries,
                        null, collected, collectedNames);
            if (info.getDependencies() != null) {
                for (SharedLibraryInfo dep : info.getDependencies()) {
                    findSharedLibrariesRecursive(dep, collected, collectedNames);
                }
            if (p.usesStaticLibraries != null) {
                findSharedNonSystemLibrariesRecursive(p.usesStaticLibraries,
                        p.usesStaticLibrariesVersions, collected, collectedNames);
            }
        }
    }
    private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, long[] versions,
            ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
        final int libNameCount = libs.size();
        for (int i = 0; i < libNameCount; i++) {
            String libName = libs.get(i);
            long version = (versions != null && versions.length == libNameCount)
                    ? versions[i] : PackageManager.VERSION_CODE_HIGHEST;
            PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version);
            if (libPkg != null) {
                findSharedNonSystemLibrariesRecursive(libPkg, collected, collectedNames);
            }
    List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package pkg) {
        List<SharedLibraryInfo> deps = findSharedLibraries(pkg);
        if (!deps.isEmpty()) {
            ArrayList<PackageParser.Package> retValue = new ArrayList<>();
            synchronized (mPackages) {
                for (SharedLibraryInfo info : deps) {
                    PackageParser.Package depPackage = mPackages.get(info.getPackageName());
                    if (depPackage != null) {
                        retValue.add(depPackage);
                    }
                }
    private PackageParser.Package findSharedNonSystemLibrary(String name, long version) {
        synchronized (mPackages) {
            SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version);
            if (libraryInfo != null) {
                return mPackages.get(libraryInfo.getPackageName());
            }
            return null;
            return retValue;
        } else {
            return Collections.emptyList();
        }
    }
+12 −0
Original line number Diff line number Diff line
@@ -173,6 +173,18 @@ public final class DexoptUtils {
        return classLoaderContexts;
    }

    /**
     * Creates the class loader context for the given shared library.
     */
    public static String getClassLoaderContext(SharedLibraryInfo info) {
        String sharedLibrariesContext = "";
        if (info.getDependencies() != null) {
            sharedLibrariesContext = encodeSharedLibraries(info.getDependencies());
        }
        return encodeClassLoader(
                "", SHARED_LIBRARY_LOADER_TYPE, sharedLibrariesContext);
    }

    /**
     * Recursive method to generate the class loader context dependencies for the split with the
     * given index. {@param classLoaderContexts} acts as an accumulator. Upton return
+14 −0
Original line number Diff line number Diff line
@@ -331,6 +331,20 @@ public class DexoptUtilsTest {
        assertEquals(null, contexts[7]);
    }

    @Test
    public void testSharedLibraryContext() {
        SharedLibraryInfo sharedLibrary =
                createMockSharedLibrary(new String[] {"a.dex", "b.dex"}).get(0);
        String context = DexoptUtils.getClassLoaderContext(sharedLibrary);
        assertEquals("PCL[]", context);

        SharedLibraryInfo otherSharedLibrary =
                createMockSharedLibrary(new String[] {"c.dex"}).get(0);
        otherSharedLibrary.addDependency(sharedLibrary);
        context = DexoptUtils.getClassLoaderContext(otherSharedLibrary);
        assertEquals("PCL[]{PCL[a.dex:b.dex]}", context);
    }

    @Test
    public void testProcessContextForDexLoad() {
        List<String> classLoaders = Arrays.asList(