Loading core/java/android/content/pm/IOtaDexopt.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,13 @@ interface IOtaDexopt { /** * Optimize the next package. Note: this command is synchronous, that is, only returns after * the package has been dexopted (or dexopting failed). * * Note: this will be removed after a transition period. Use nextDexoptCommand instead. */ void dexoptNextPackage(); /** * Get the optimization parameters for the next package. */ String nextDexoptCommand(); } services/core/java/com/android/server/pm/Installer.java +7 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,13 @@ public final class Installer extends SystemService { 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 * the given object. Loading services/core/java/com/android/server/pm/OtaDexoptService.java +152 −9 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } Loading Loading @@ -93,6 +102,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { mPackageManagerService.mPackages.values(), mPackageManagerService); } completeSize = mDexoptPackages.size(); mCommandsForCurrentPackage = null; } @Override Loading @@ -101,6 +111,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { Log.i(TAG, "Cleaning up OTA Dexopt state."); } mDexoptPackages = null; mCommandsForCurrentPackage = null; } @Override Loading @@ -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 Loading Loading @@ -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)); } Loading Loading @@ -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"); } } } services/core/java/com/android/server/pm/OtaDexoptShellCommand.java +9 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,8 @@ class OtaDexoptShellCommand extends ShellCommand { return runOtaDone(); case "step": return runOtaStep(); case "next": return runOtaNext(); case "progress": return runOtaProgress(); default: Loading Loading @@ -83,6 +85,11 @@ class OtaDexoptShellCommand extends ShellCommand { return 0; } private int runOtaNext() throws RemoteException { getOutPrintWriter().println(mInterface.nextDexoptCommand()); return 0; } private int runOtaProgress() throws RemoteException { final float progress = mInterface.getProgress(); final PrintWriter pw = getOutPrintWriter(); Loading @@ -103,6 +110,8 @@ class OtaDexoptShellCommand extends ShellCommand { pw.println(" Replies whether the OTA is complete or not."); pw.println(" step"); 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(" Clean up internal states. Ends an OTA session."); } Loading Loading
core/java/android/content/pm/IOtaDexopt.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,13 @@ interface IOtaDexopt { /** * Optimize the next package. Note: this command is synchronous, that is, only returns after * the package has been dexopted (or dexopting failed). * * Note: this will be removed after a transition period. Use nextDexoptCommand instead. */ void dexoptNextPackage(); /** * Get the optimization parameters for the next package. */ String nextDexoptCommand(); }
services/core/java/com/android/server/pm/Installer.java +7 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,13 @@ public final class Installer extends SystemService { 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 * the given object. Loading
services/core/java/com/android/server/pm/OtaDexoptService.java +152 −9 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } Loading Loading @@ -93,6 +102,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { mPackageManagerService.mPackages.values(), mPackageManagerService); } completeSize = mDexoptPackages.size(); mCommandsForCurrentPackage = null; } @Override Loading @@ -101,6 +111,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { Log.i(TAG, "Cleaning up OTA Dexopt state."); } mDexoptPackages = null; mCommandsForCurrentPackage = null; } @Override Loading @@ -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 Loading Loading @@ -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)); } Loading Loading @@ -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"); } } }
services/core/java/com/android/server/pm/OtaDexoptShellCommand.java +9 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,8 @@ class OtaDexoptShellCommand extends ShellCommand { return runOtaDone(); case "step": return runOtaStep(); case "next": return runOtaNext(); case "progress": return runOtaProgress(); default: Loading Loading @@ -83,6 +85,11 @@ class OtaDexoptShellCommand extends ShellCommand { return 0; } private int runOtaNext() throws RemoteException { getOutPrintWriter().println(mInterface.nextDexoptCommand()); return 0; } private int runOtaProgress() throws RemoteException { final float progress = mInterface.getProgress(); final PrintWriter pw = getOutPrintWriter(); Loading @@ -103,6 +110,8 @@ class OtaDexoptShellCommand extends ShellCommand { pw.println(" Replies whether the OTA is complete or not."); pw.println(" step"); 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(" Clean up internal states. Ends an OTA session."); } Loading