Loading services/core/java/com/android/server/pm/OtaDexoptService.java +64 −125 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; } // Move to the next package, if possible. if (mDexoptPackages.isEmpty()) { return "Nothing to do"; if (mDexoptCommands.isEmpty()) { return "(all done)"; } PackageParser.Package nextPackage = mDexoptPackages.remove(0); if (DEBUG_DEXOPT) { Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt."); } String next = mDexoptCommands.remove(0); // 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(); Loading @@ -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); Loading @@ -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."); } Loading Loading
services/core/java/com/android/server/pm/OtaDexoptService.java +64 −125 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; } // Move to the next package, if possible. if (mDexoptPackages.isEmpty()) { return "Nothing to do"; if (mDexoptCommands.isEmpty()) { return "(all done)"; } PackageParser.Package nextPackage = mDexoptPackages.remove(0); if (DEBUG_DEXOPT) { Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt."); } String next = mDexoptCommands.remove(0); // 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(); Loading @@ -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); Loading @@ -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."); } Loading