Merge "OtaDexoptService: Run dexopt on all apps" into nyc-mr1-dev

This commit is contained in:
TreeHugger Robot
2016-07-29 22:41:37 +00:00
committed by Android (Google) Code Review

View File

@@ -56,16 +56,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
// TODO: Evaluate the need for WeakReferences here.
/**
* The list of packages to dexopt.
* The list of dexopt invocations for all work.
*/
private List<PackageParser.Package> mDexoptPackages;
/**
* The list of dexopt invocations for the current package (which will no longer be in
* mDexoptPackages). This can be more than one as a package may have multiple code paths,
* e.g., in the split-APK case.
*/
private List<String> mCommandsForCurrentPackage;
private List<String> mDexoptCommands;
private int completeSize;
@@ -94,15 +87,43 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
@Override
public synchronized void prepare() throws RemoteException {
if (mDexoptPackages != null) {
if (mDexoptCommands != null) {
throw new IllegalStateException("already called prepare()");
}
synchronized (mPackageManagerService.mPackages) {
mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
// Important: the packages we need to run with ab-ota compiler-reason.
List<PackageParser.Package> important = PackageManagerServiceUtils.getPackagesForDexopt(
mPackageManagerService.mPackages.values(), mPackageManagerService);
// Others: we should optimize this with the (first-)boot compiler-reason.
List<PackageParser.Package> others =
new ArrayList<>(mPackageManagerService.mPackages.values());
others.removeAll(important);
// Pre-size the array list by over-allocating by a factor of 1.5.
mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
for (PackageParser.Package p : important) {
// Make sure that core apps are optimized according to their own "reason".
// If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
// (by default is speed-profile) they will be interepreted/JITed. This in itself is
// not a problem as we will end up doing profile guided compilation. However, some
// core apps may be loaded by system server which doesn't JIT and we need to make
// sure we don't interpret-only
int compilationReason = p.coreApp
? PackageManagerService.REASON_CORE_APP
: PackageManagerService.REASON_AB_OTA;
mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason));
}
for (PackageParser.Package p : others) {
// We assume here that there are no core apps left.
if (p.coreApp) {
throw new IllegalStateException("Found a core app that's not important");
}
mDexoptCommands.addAll(
generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
}
}
completeSize = mDexoptPackages.size();
mCommandsForCurrentPackage = null;
completeSize = mDexoptCommands.size();
}
@Override
@@ -110,87 +131,52 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Cleaning up OTA Dexopt state.");
}
mDexoptPackages = null;
mCommandsForCurrentPackage = null;
mDexoptCommands = null;
}
@Override
public synchronized boolean isDone() throws RemoteException {
if (mDexoptPackages == null) {
if (mDexoptCommands == null) {
throw new IllegalStateException("done() called before prepare()");
}
return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null);
return mDexoptCommands.isEmpty();
}
@Override
public synchronized float getProgress() throws RemoteException {
// We approximate by number of packages here. We could track all compiles, if we
// generated them ahead of time. Right now we're trying to conserve memory.
// Approximate the progress by the amount of already completed commands.
if (completeSize == 0) {
return 1f;
}
int packagesLeft = mDexoptPackages.size() + (mCommandsForCurrentPackage != null ? 1 : 0);
return (completeSize - packagesLeft) / ((float)completeSize);
}
/**
* Return the next dexopt command for the current package. Enforces the invariant
*/
private String getNextPackageDexopt() {
if (mCommandsForCurrentPackage != null) {
String next = mCommandsForCurrentPackage.remove(0);
if (mCommandsForCurrentPackage.isEmpty()) {
mCommandsForCurrentPackage = null;
}
return next;
}
return null;
int commandsLeft = mDexoptCommands.size();
return (completeSize - commandsLeft) / ((float)completeSize);
}
@Override
public synchronized String nextDexoptCommand() throws RemoteException {
if (mDexoptPackages == null) {
if (mDexoptCommands == null) {
throw new IllegalStateException("dexoptNextPackage() called before prepare()");
}
// Get the next command.
for (;;) {
// Check whether there's one for the current package.
String next = getNextPackageDexopt();
if (next != null) {
return next;
}
if (mDexoptCommands.isEmpty()) {
return "(all done)";
}
// Move to the next package, if possible.
if (mDexoptPackages.isEmpty()) {
return "Nothing to do";
}
String next = mDexoptCommands.remove(0);
PackageParser.Package nextPackage = mDexoptPackages.remove(0);
if (DEBUG_DEXOPT) {
Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
}
// Generate the next mPackageDexopts state. Ignore errors, this loop is strongly
// monotonically increasing, anyways.
generatePackageDexopts(nextPackage);
// Invariant check: mPackageDexopts is null or not empty.
if (mCommandsForCurrentPackage != null && mCommandsForCurrentPackage.isEmpty()) {
cleanup();
throw new IllegalStateException("mPackageDexopts empty for " + nextPackage);
}
if (IsFreeSpaceAvailable()) {
return next;
} else {
mDexoptCommands.clear();
return "(no free space)";
}
}
/**
* Generate all dexopt commands for the given package and place them into mPackageDexopts.
* Returns true on success, false in an error situation like low disk space.
* Check for low space. Returns true if there's space left.
*/
private synchronized boolean generatePackageDexopts(PackageParser.Package nextPackage) {
// Check for low space.
private boolean IsFreeSpaceAvailable() {
// TODO: If apps are not installed in the internal /data partition, we should compare
// against that storage's free capacity.
File dataDir = Environment.getDataDirectory();
@@ -200,12 +186,14 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
throw new IllegalStateException("Invalid low memory threshold");
}
long usableSpace = dataDir.getUsableSpace();
if (usableSpace < lowThreshold) {
Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
usableSpace);
return false;
}
return (usableSpace >= lowThreshold);
}
/**
* Generate all dexopt commands for the given package.
*/
private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
int compilationReason) {
// Use our custom connection that just collects the commands.
RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
Installer collectingInstaller = new Installer(mContext, collectingConnection);
@@ -213,71 +201,22 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
// Use the package manager install and install lock here for the OTA dex optimizer.
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
// Make sure that core apps are optimized according to their own "reason".
// If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
// (by default is speed-profile) they will be interepreted/JITed. This in itself is not a
// problem as we will end up doing profile guided compilation. However, some core apps may
// be loaded by system server which doesn't JIT and we need to make sure we don't
// interpret-only
int compilationReason = nextPackage.coreApp
? PackageManagerService.REASON_CORE_APP
: PackageManagerService.REASON_AB_OTA;
optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
optimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* ISAs */, false /* checkProfiles */,
getCompilerFilterForReason(compilationReason),
null /* CompilerStats.PackageStats */);
mCommandsForCurrentPackage = collectingConnection.commands;
if (mCommandsForCurrentPackage.isEmpty()) {
mCommandsForCurrentPackage = null;
}
return true;
return collectingConnection.commands;
}
@Override
public synchronized void dexoptNextPackage() throws RemoteException {
if (mDexoptPackages == null) {
throw new IllegalStateException("dexoptNextPackage() called before prepare()");
}
if (mDexoptPackages.isEmpty()) {
// Tolerate repeated calls.
return;
}
PackageParser.Package nextPackage = mDexoptPackages.remove(0);
if (DEBUG_DEXOPT) {
Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
}
// Check for low space.
// TODO: If apps are not installed in the internal /data partition, we should compare
// against that storage's free capacity.
File dataDir = Environment.getDataDirectory();
@SuppressWarnings("deprecation")
long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
if (lowThreshold == 0) {
throw new IllegalStateException("Invalid low memory threshold");
}
long usableSpace = dataDir.getUsableSpace();
if (usableSpace < lowThreshold) {
Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
usableSpace);
return;
}
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
mPackageManagerService.mInstaller, mPackageManagerService.mInstallLock, mContext);
optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, null /* ISAs */,
false /* checkProfiles */,
getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA),
mPackageManagerService.getOrCreateCompilerPackageStats(nextPackage));
throw new UnsupportedOperationException();
}
private void moveAbArtifacts(Installer installer) {
if (mDexoptPackages != null) {
if (mDexoptCommands != null) {
throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
}