OtaDexoptService: Run dexopt on all apps
am: 14186731f9
Change-Id: I6f53fc0acaa8f5413ac70488e4bd9938a3253cbf
This commit is contained in:
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user