|
|
|
|
@@ -32,10 +32,12 @@ import android.os.storage.StorageManager;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.util.Slog;
|
|
|
|
|
|
|
|
|
|
import com.android.internal.os.InstallerConnection;
|
|
|
|
|
import com.android.internal.os.InstallerConnection.InstallerException;
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.FileDescriptor;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
@@ -49,21 +51,28 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|
|
|
|
private final static boolean DEBUG_DEXOPT = true;
|
|
|
|
|
|
|
|
|
|
private final Context mContext;
|
|
|
|
|
private final PackageDexOptimizer mPackageDexOptimizer;
|
|
|
|
|
private final PackageManagerService mPackageManagerService;
|
|
|
|
|
|
|
|
|
|
// TODO: Evaluate the need for WeakReferences here.
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The list of packages to dexopt.
|
|
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
|
|
|
|
|
this.mContext = context;
|
|
|
|
|
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.
|
|
|
|
|
moveAbArtifacts(packageManagerService.mInstaller);
|
|
|
|
|
}
|
|
|
|
|
@@ -93,6 +102,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|
|
|
|
mPackageManagerService.mPackages.values(), mPackageManagerService);
|
|
|
|
|
}
|
|
|
|
|
completeSize = mDexoptPackages.size();
|
|
|
|
|
mCommandsForCurrentPackage = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@@ -101,6 +111,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|
|
|
|
Log.i(TAG, "Cleaning up OTA Dexopt state.");
|
|
|
|
|
}
|
|
|
|
|
mDexoptPackages = null;
|
|
|
|
|
mCommandsForCurrentPackage = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@@ -109,15 +120,109 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|
|
|
|
throw new IllegalStateException("done() called before prepare()");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mDexoptPackages.isEmpty();
|
|
|
|
|
return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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.
|
|
|
|
|
if (completeSize == 0) {
|
|
|
|
|
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
|
|
|
|
|
@@ -152,8 +257,10 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
|
|
|
|
|
null /* ISAs */, false /* checkProfiles */,
|
|
|
|
|
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
|
|
|
|
|
mPackageManagerService.mInstaller, mPackageManagerService.mInstallLock, mContext);
|
|
|
|
|
optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, null /* ISAs */,
|
|
|
|
|
false /* checkProfiles */,
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|