Merge "Support dexopting shared libraries."
This commit is contained in:
@@ -25,10 +25,12 @@ import android.content.pm.dex.ArtManager;
|
|||||||
import android.content.pm.dex.DexMetadataHelper;
|
import android.content.pm.dex.DexMetadataHelper;
|
||||||
import android.os.FileUtils;
|
import android.os.FileUtils;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
import android.os.Process;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.WorkSource;
|
import android.os.WorkSource;
|
||||||
|
import android.os.storage.StorageManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
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.
|
* Performs dexopt on all code paths of the given package.
|
||||||
* It assumes the install lock is held.
|
* It assumes the install lock is held.
|
||||||
|
|||||||
@@ -9443,18 +9443,27 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
|
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
|
||||||
// and the first package that uses the library will dexopt it. The
|
// 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.
|
// 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);
|
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
|
||||||
if (!deps.isEmpty()) {
|
if (!deps.isEmpty()) {
|
||||||
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
|
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
|
||||||
options.getCompilationReason(), options.getCompilerFilter(),
|
options.getCompilationReason(), options.getCompilerFilter(),
|
||||||
options.getSplitName(),
|
options.getSplitName(),
|
||||||
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
|
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
|
||||||
for (PackageParser.Package depPackage : deps) {
|
for (SharedLibraryInfo info : deps) {
|
||||||
// TODO: Analyze and investigate if we (should) profile libraries.
|
PackageParser.Package depPackage = null;
|
||||||
pdo.performDexOpt(depPackage, instructionSets,
|
synchronized (mPackages) {
|
||||||
getOrCreateCompilerPackageStats(depPackage),
|
depPackage = mPackages.get(info.getPackageName());
|
||||||
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
|
}
|
||||||
|
if (depPackage != null) {
|
||||||
|
// TODO: Analyze and investigate if we (should) profile libraries.
|
||||||
|
pdo.performDexOpt(depPackage, instructionSets,
|
||||||
|
getOrCreateCompilerPackageStats(depPackage),
|
||||||
|
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName),
|
||||||
|
libraryOptions);
|
||||||
|
} else {
|
||||||
|
pdo.performDexOpt(info, instructionSets, libraryOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pdo.performDexOpt(p, instructionSets,
|
return pdo.performDexOpt(p, instructionSets,
|
||||||
@@ -9494,63 +9503,48 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
|
return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
|
private static List<SharedLibraryInfo> findSharedLibraries(PackageParser.Package p) {
|
||||||
if (p.usesLibraries != null || p.usesOptionalLibraries != null
|
if (p.usesLibraryInfos != null) {
|
||||||
|| p.usesStaticLibraries != null) {
|
ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
|
||||||
ArrayList<PackageParser.Package> retValue = new ArrayList<>();
|
|
||||||
Set<String> collectedNames = new HashSet<>();
|
Set<String> collectedNames = new HashSet<>();
|
||||||
findSharedNonSystemLibrariesRecursive(p, retValue, collectedNames);
|
for (SharedLibraryInfo info : p.usesLibraryInfos) {
|
||||||
|
findSharedLibrariesRecursive(info, retValue, collectedNames);
|
||||||
retValue.remove(p);
|
}
|
||||||
|
|
||||||
return retValue;
|
return retValue;
|
||||||
} else {
|
} else {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findSharedNonSystemLibrariesRecursive(PackageParser.Package p,
|
private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
|
||||||
ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
|
ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
|
||||||
if (!collectedNames.contains(p.packageName)) {
|
if (!collectedNames.contains(info.getName())) {
|
||||||
collectedNames.add(p.packageName);
|
collectedNames.add(info.getName());
|
||||||
collected.add(p);
|
collected.add(info);
|
||||||
|
|
||||||
if (p.usesLibraries != null) {
|
if (info.getDependencies() != null) {
|
||||||
findSharedNonSystemLibrariesRecursive(p.usesLibraries,
|
for (SharedLibraryInfo dep : info.getDependencies()) {
|
||||||
null, collected, collectedNames);
|
findSharedLibrariesRecursive(dep, collected, collectedNames);
|
||||||
}
|
}
|
||||||
if (p.usesOptionalLibraries != null) {
|
|
||||||
findSharedNonSystemLibrariesRecursive(p.usesOptionalLibraries,
|
|
||||||
null, collected, collectedNames);
|
|
||||||
}
|
|
||||||
if (p.usesStaticLibraries != null) {
|
|
||||||
findSharedNonSystemLibrariesRecursive(p.usesStaticLibraries,
|
|
||||||
p.usesStaticLibrariesVersions, collected, collectedNames);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, long[] versions,
|
List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package pkg) {
|
||||||
ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
|
List<SharedLibraryInfo> deps = findSharedLibraries(pkg);
|
||||||
final int libNameCount = libs.size();
|
if (!deps.isEmpty()) {
|
||||||
for (int i = 0; i < libNameCount; i++) {
|
ArrayList<PackageParser.Package> retValue = new ArrayList<>();
|
||||||
String libName = libs.get(i);
|
synchronized (mPackages) {
|
||||||
long version = (versions != null && versions.length == libNameCount)
|
for (SharedLibraryInfo info : deps) {
|
||||||
? versions[i] : PackageManager.VERSION_CODE_HIGHEST;
|
PackageParser.Package depPackage = mPackages.get(info.getPackageName());
|
||||||
PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version);
|
if (depPackage != null) {
|
||||||
if (libPkg != null) {
|
retValue.add(depPackage);
|
||||||
findSharedNonSystemLibrariesRecursive(libPkg, collected, collectedNames);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return retValue;
|
||||||
}
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,18 @@ public final class DexoptUtils {
|
|||||||
return classLoaderContexts;
|
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
|
* Recursive method to generate the class loader context dependencies for the split with the
|
||||||
* given index. {@param classLoaderContexts} acts as an accumulator. Upton return
|
* given index. {@param classLoaderContexts} acts as an accumulator. Upton return
|
||||||
|
|||||||
@@ -331,6 +331,20 @@ public class DexoptUtilsTest {
|
|||||||
assertEquals(null, contexts[7]);
|
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
|
@Test
|
||||||
public void testProcessContextForDexLoad() {
|
public void testProcessContextForDexLoad() {
|
||||||
List<String> classLoaders = Arrays.asList(
|
List<String> classLoaders = Arrays.asList(
|
||||||
|
|||||||
Reference in New Issue
Block a user