Loading services/core/java/com/android/server/pm/PackageDexOptimizer.java +47 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading services/core/java/com/android/server/pm/PackageManagerService.java +44 −50 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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(); } } services/core/java/com/android/server/pm/dex/DexoptUtils.java +12 −0 Original line number Diff line number Diff line Loading @@ -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 Loading services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +14 −0 Original line number Diff line number Diff line Loading @@ -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( Loading Loading
services/core/java/com/android/server/pm/PackageDexOptimizer.java +47 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading
services/core/java/com/android/server/pm/PackageManagerService.java +44 −50 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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(); } }
services/core/java/com/android/server/pm/dex/DexoptUtils.java +12 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +14 −0 Original line number Diff line number Diff line Loading @@ -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( Loading