Frameworks/base: Add new flow to OtaDexoptService
am: d15300cf38
Change-Id: I74f9938dc74c19ee0ef220ab3b50daa1085edea2
This commit is contained in:
@@ -50,6 +50,13 @@ interface IOtaDexopt {
|
|||||||
/**
|
/**
|
||||||
* Optimize the next package. Note: this command is synchronous, that is, only returns after
|
* Optimize the next package. Note: this command is synchronous, that is, only returns after
|
||||||
* the package has been dexopted (or dexopting failed).
|
* the package has been dexopted (or dexopting failed).
|
||||||
|
*
|
||||||
|
* Note: this will be removed after a transition period. Use nextDexoptCommand instead.
|
||||||
*/
|
*/
|
||||||
void dexoptNextPackage();
|
void dexoptNextPackage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the optimization parameters for the next package.
|
||||||
|
*/
|
||||||
|
String nextDexoptCommand();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,13 @@ public final class Installer extends SystemService {
|
|||||||
mInstaller = new InstallerConnection();
|
mInstaller = new InstallerConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Package-private installer that accepts a custom InstallerConnection. Used for
|
||||||
|
// OtaDexoptService.
|
||||||
|
Installer(Context context, InstallerConnection connection) {
|
||||||
|
super(context);
|
||||||
|
mInstaller = connection;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Yell loudly if someone tries making future calls while holding a lock on
|
* Yell loudly if someone tries making future calls while holding a lock on
|
||||||
* the given object.
|
* the given object.
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ import android.os.storage.StorageManager;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.internal.os.InstallerConnection;
|
||||||
import com.android.internal.os.InstallerConnection.InstallerException;
|
import com.android.internal.os.InstallerConnection.InstallerException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -49,21 +51,28 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|||||||
private final static boolean DEBUG_DEXOPT = true;
|
private final static boolean DEBUG_DEXOPT = true;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final PackageDexOptimizer mPackageDexOptimizer;
|
|
||||||
private final PackageManagerService mPackageManagerService;
|
private final PackageManagerService mPackageManagerService;
|
||||||
|
|
||||||
// TODO: Evaluate the need for WeakReferences here.
|
// TODO: Evaluate the need for WeakReferences here.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of packages to dexopt.
|
||||||
|
*/
|
||||||
private List<PackageParser.Package> mDexoptPackages;
|
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 int completeSize;
|
private int completeSize;
|
||||||
|
|
||||||
public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
|
public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
this.mPackageManagerService = packageManagerService;
|
this.mPackageManagerService = packageManagerService;
|
||||||
|
|
||||||
// Use the package manager install and install lock here for the OTA dex optimizer.
|
|
||||||
mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller,
|
|
||||||
packageManagerService.mInstallLock, context);
|
|
||||||
|
|
||||||
// Now it's time to check whether we need to move any A/B artifacts.
|
// Now it's time to check whether we need to move any A/B artifacts.
|
||||||
moveAbArtifacts(packageManagerService.mInstaller);
|
moveAbArtifacts(packageManagerService.mInstaller);
|
||||||
}
|
}
|
||||||
@@ -93,6 +102,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|||||||
mPackageManagerService.mPackages.values(), mPackageManagerService);
|
mPackageManagerService.mPackages.values(), mPackageManagerService);
|
||||||
}
|
}
|
||||||
completeSize = mDexoptPackages.size();
|
completeSize = mDexoptPackages.size();
|
||||||
|
mCommandsForCurrentPackage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,6 +111,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|||||||
Log.i(TAG, "Cleaning up OTA Dexopt state.");
|
Log.i(TAG, "Cleaning up OTA Dexopt state.");
|
||||||
}
|
}
|
||||||
mDexoptPackages = null;
|
mDexoptPackages = null;
|
||||||
|
mCommandsForCurrentPackage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,15 +120,109 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|||||||
throw new IllegalStateException("done() called before prepare()");
|
throw new IllegalStateException("done() called before prepare()");
|
||||||
}
|
}
|
||||||
|
|
||||||
return mDexoptPackages.isEmpty();
|
return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized float getProgress() throws RemoteException {
|
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.
|
||||||
if (completeSize == 0) {
|
if (completeSize == 0) {
|
||||||
return 1f;
|
return 1f;
|
||||||
}
|
}
|
||||||
return (completeSize - mDexoptPackages.size()) / ((float)completeSize);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String nextDexoptCommand() throws RemoteException {
|
||||||
|
if (mDexoptPackages == 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next package, if possible.
|
||||||
|
if (mDexoptPackages.isEmpty()) {
|
||||||
|
return "Nothing to do";
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private synchronized boolean generatePackageDexopts(PackageParser.Package nextPackage) {
|
||||||
|
// 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 false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use our custom connection that just collects the commands.
|
||||||
|
RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
|
||||||
|
Installer collectingInstaller = new Installer(mContext, collectingConnection);
|
||||||
|
|
||||||
|
// Use the package manager install and install lock here for the OTA dex optimizer.
|
||||||
|
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
|
||||||
|
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
|
||||||
|
optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
|
||||||
|
null /* ISAs */, false /* checkProfiles */,
|
||||||
|
getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
|
||||||
|
|
||||||
|
mCommandsForCurrentPackage = collectingConnection.commands;
|
||||||
|
if (mCommandsForCurrentPackage.isEmpty()) {
|
||||||
|
mCommandsForCurrentPackage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,8 +257,10 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
|
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
|
||||||
null /* ISAs */, false /* checkProfiles */,
|
mPackageManagerService.mInstaller, mPackageManagerService.mInstallLock, mContext);
|
||||||
|
optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, null /* ISAs */,
|
||||||
|
false /* checkProfiles */,
|
||||||
getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
|
getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,4 +325,40 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class RecordingInstallerConnection extends InstallerConnection {
|
||||||
|
public List<String> commands = new ArrayList<String>(1);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWarnIfHeld(Object warnIfHeld) {
|
||||||
|
throw new IllegalStateException("Should not reach here");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String transact(String cmd) {
|
||||||
|
commands.add(cmd);
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
|
||||||
|
throw new IllegalStateException("Should not reach here");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dumpProfiles(String gid, String packageName, String codePaths)
|
||||||
|
throws InstallerException {
|
||||||
|
throw new IllegalStateException("Should not reach here");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
throw new IllegalStateException("Should not reach here");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void waitForConnection() {
|
||||||
|
throw new IllegalStateException("Should not reach here");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class OtaDexoptShellCommand extends ShellCommand {
|
|||||||
return runOtaDone();
|
return runOtaDone();
|
||||||
case "step":
|
case "step":
|
||||||
return runOtaStep();
|
return runOtaStep();
|
||||||
|
case "next":
|
||||||
|
return runOtaNext();
|
||||||
case "progress":
|
case "progress":
|
||||||
return runOtaProgress();
|
return runOtaProgress();
|
||||||
default:
|
default:
|
||||||
@@ -83,6 +85,11 @@ class OtaDexoptShellCommand extends ShellCommand {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int runOtaNext() throws RemoteException {
|
||||||
|
getOutPrintWriter().println(mInterface.nextDexoptCommand());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private int runOtaProgress() throws RemoteException {
|
private int runOtaProgress() throws RemoteException {
|
||||||
final float progress = mInterface.getProgress();
|
final float progress = mInterface.getProgress();
|
||||||
final PrintWriter pw = getOutPrintWriter();
|
final PrintWriter pw = getOutPrintWriter();
|
||||||
@@ -103,6 +110,8 @@ class OtaDexoptShellCommand extends ShellCommand {
|
|||||||
pw.println(" Replies whether the OTA is complete or not.");
|
pw.println(" Replies whether the OTA is complete or not.");
|
||||||
pw.println(" step");
|
pw.println(" step");
|
||||||
pw.println(" OTA dexopt the next package.");
|
pw.println(" OTA dexopt the next package.");
|
||||||
|
pw.println(" next");
|
||||||
|
pw.println(" Get parameters for OTA dexopt of the next package.");
|
||||||
pw.println(" cleanup");
|
pw.println(" cleanup");
|
||||||
pw.println(" Clean up internal states. Ends an OTA session.");
|
pw.println(" Clean up internal states. Ends an OTA session.");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user