Loading services/core/java/com/android/server/pm/OtaDexoptService.java +64 −125 Original line number Original line Diff line number Diff line Loading @@ -56,16 +56,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub { // TODO: Evaluate the need for WeakReferences here. // 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; private List<String> mDexoptCommands; /** * 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; Loading Loading @@ -94,15 +87,43 @@ public class OtaDexoptService extends IOtaDexopt.Stub { @Override @Override public synchronized void prepare() throws RemoteException { public synchronized void prepare() throws RemoteException { if (mDexoptPackages != null) { if (mDexoptCommands != null) { throw new IllegalStateException("already called prepare()"); throw new IllegalStateException("already called prepare()"); } } synchronized (mPackageManagerService.mPackages) { 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); 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 @Override Loading @@ -110,87 +131,52 @@ public class OtaDexoptService extends IOtaDexopt.Stub { if (DEBUG_DEXOPT) { if (DEBUG_DEXOPT) { Log.i(TAG, "Cleaning up OTA Dexopt state."); Log.i(TAG, "Cleaning up OTA Dexopt state."); } } mDexoptPackages = null; mDexoptCommands = null; mCommandsForCurrentPackage = null; } } @Override @Override public synchronized boolean isDone() throws RemoteException { public synchronized boolean isDone() throws RemoteException { if (mDexoptPackages == null) { if (mDexoptCommands == null) { throw new IllegalStateException("done() called before prepare()"); throw new IllegalStateException("done() called before prepare()"); } } return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null); return mDexoptCommands.isEmpty(); } } @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 // Approximate the progress by the amount of already completed commands. // generated them ahead of time. Right now we're trying to conserve memory. if (completeSize == 0) { if (completeSize == 0) { return 1f; return 1f; } } int packagesLeft = mDexoptPackages.size() + (mCommandsForCurrentPackage != null ? 1 : 0); int commandsLeft = mDexoptCommands.size(); return (completeSize - packagesLeft) / ((float)completeSize); return (completeSize - commandsLeft) / ((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 @Override public synchronized String nextDexoptCommand() throws RemoteException { public synchronized String nextDexoptCommand() throws RemoteException { if (mDexoptPackages == null) { if (mDexoptCommands == null) { throw new IllegalStateException("dexoptNextPackage() called before prepare()"); throw new IllegalStateException("dexoptNextPackage() called before prepare()"); } } // Get the next command. if (mDexoptCommands.isEmpty()) { for (;;) { return "(all done)"; // 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); String next = mDexoptCommands.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 if (IsFreeSpaceAvailable()) { // monotonically increasing, anyways. return next; generatePackageDexopts(nextPackage); } else { mDexoptCommands.clear(); // Invariant check: mPackageDexopts is null or not empty. return "(no free space)"; 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. * Check for low space. Returns true if there's space left. * Returns true on success, false in an error situation like low disk space. */ */ private synchronized boolean generatePackageDexopts(PackageParser.Package nextPackage) { private boolean IsFreeSpaceAvailable() { // Check for low space. // TODO: If apps are not installed in the internal /data partition, we should compare // TODO: If apps are not installed in the internal /data partition, we should compare // against that storage's free capacity. // against that storage's free capacity. File dataDir = Environment.getDataDirectory(); File dataDir = Environment.getDataDirectory(); Loading @@ -200,12 +186,14 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throw new IllegalStateException("Invalid low memory threshold"); throw new IllegalStateException("Invalid low memory threshold"); } } long usableSpace = dataDir.getUsableSpace(); long usableSpace = dataDir.getUsableSpace(); if (usableSpace < lowThreshold) { return (usableSpace >= lowThreshold); Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " + usableSpace); return false; } } /** * 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. // Use our custom connection that just collects the commands. RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection(); RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection(); Installer collectingInstaller = new Installer(mContext, collectingConnection); 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. // Use the package manager install and install lock here for the OTA dex optimizer. PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( collectingInstaller, mPackageManagerService.mInstallLock, mContext); 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 */, null /* ISAs */, false /* checkProfiles */, getCompilerFilterForReason(compilationReason), getCompilerFilterForReason(compilationReason), null /* CompilerStats.PackageStats */); null /* CompilerStats.PackageStats */); mCommandsForCurrentPackage = collectingConnection.commands; return collectingConnection.commands; if (mCommandsForCurrentPackage.isEmpty()) { mCommandsForCurrentPackage = null; } return true; } } @Override @Override public synchronized void dexoptNextPackage() throws RemoteException { public synchronized void dexoptNextPackage() throws RemoteException { if (mDexoptPackages == null) { throw new UnsupportedOperationException(); 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)); } } private void moveAbArtifacts(Installer installer) { private void moveAbArtifacts(Installer installer) { if (mDexoptPackages != null) { if (mDexoptCommands != null) { throw new IllegalStateException("Should not be ota-dexopting when trying to move."); 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 Original line Diff line number Diff line Loading @@ -56,16 +56,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub { // TODO: Evaluate the need for WeakReferences here. // 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; private List<String> mDexoptCommands; /** * 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; Loading Loading @@ -94,15 +87,43 @@ public class OtaDexoptService extends IOtaDexopt.Stub { @Override @Override public synchronized void prepare() throws RemoteException { public synchronized void prepare() throws RemoteException { if (mDexoptPackages != null) { if (mDexoptCommands != null) { throw new IllegalStateException("already called prepare()"); throw new IllegalStateException("already called prepare()"); } } synchronized (mPackageManagerService.mPackages) { 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); 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 @Override Loading @@ -110,87 +131,52 @@ public class OtaDexoptService extends IOtaDexopt.Stub { if (DEBUG_DEXOPT) { if (DEBUG_DEXOPT) { Log.i(TAG, "Cleaning up OTA Dexopt state."); Log.i(TAG, "Cleaning up OTA Dexopt state."); } } mDexoptPackages = null; mDexoptCommands = null; mCommandsForCurrentPackage = null; } } @Override @Override public synchronized boolean isDone() throws RemoteException { public synchronized boolean isDone() throws RemoteException { if (mDexoptPackages == null) { if (mDexoptCommands == null) { throw new IllegalStateException("done() called before prepare()"); throw new IllegalStateException("done() called before prepare()"); } } return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null); return mDexoptCommands.isEmpty(); } } @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 // Approximate the progress by the amount of already completed commands. // generated them ahead of time. Right now we're trying to conserve memory. if (completeSize == 0) { if (completeSize == 0) { return 1f; return 1f; } } int packagesLeft = mDexoptPackages.size() + (mCommandsForCurrentPackage != null ? 1 : 0); int commandsLeft = mDexoptCommands.size(); return (completeSize - packagesLeft) / ((float)completeSize); return (completeSize - commandsLeft) / ((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 @Override public synchronized String nextDexoptCommand() throws RemoteException { public synchronized String nextDexoptCommand() throws RemoteException { if (mDexoptPackages == null) { if (mDexoptCommands == null) { throw new IllegalStateException("dexoptNextPackage() called before prepare()"); throw new IllegalStateException("dexoptNextPackage() called before prepare()"); } } // Get the next command. if (mDexoptCommands.isEmpty()) { for (;;) { return "(all done)"; // 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); String next = mDexoptCommands.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 if (IsFreeSpaceAvailable()) { // monotonically increasing, anyways. return next; generatePackageDexopts(nextPackage); } else { mDexoptCommands.clear(); // Invariant check: mPackageDexopts is null or not empty. return "(no free space)"; 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. * Check for low space. Returns true if there's space left. * Returns true on success, false in an error situation like low disk space. */ */ private synchronized boolean generatePackageDexopts(PackageParser.Package nextPackage) { private boolean IsFreeSpaceAvailable() { // Check for low space. // TODO: If apps are not installed in the internal /data partition, we should compare // TODO: If apps are not installed in the internal /data partition, we should compare // against that storage's free capacity. // against that storage's free capacity. File dataDir = Environment.getDataDirectory(); File dataDir = Environment.getDataDirectory(); Loading @@ -200,12 +186,14 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throw new IllegalStateException("Invalid low memory threshold"); throw new IllegalStateException("Invalid low memory threshold"); } } long usableSpace = dataDir.getUsableSpace(); long usableSpace = dataDir.getUsableSpace(); if (usableSpace < lowThreshold) { return (usableSpace >= lowThreshold); Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " + usableSpace); return false; } } /** * 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. // Use our custom connection that just collects the commands. RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection(); RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection(); Installer collectingInstaller = new Installer(mContext, collectingConnection); 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. // Use the package manager install and install lock here for the OTA dex optimizer. PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( collectingInstaller, mPackageManagerService.mInstallLock, mContext); 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 */, null /* ISAs */, false /* checkProfiles */, getCompilerFilterForReason(compilationReason), getCompilerFilterForReason(compilationReason), null /* CompilerStats.PackageStats */); null /* CompilerStats.PackageStats */); mCommandsForCurrentPackage = collectingConnection.commands; return collectingConnection.commands; if (mCommandsForCurrentPackage.isEmpty()) { mCommandsForCurrentPackage = null; } return true; } } @Override @Override public synchronized void dexoptNextPackage() throws RemoteException { public synchronized void dexoptNextPackage() throws RemoteException { if (mDexoptPackages == null) { throw new UnsupportedOperationException(); 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)); } } private void moveAbArtifacts(Installer installer) { private void moveAbArtifacts(Installer installer) { if (mDexoptPackages != null) { if (mDexoptCommands != null) { throw new IllegalStateException("Should not be ota-dexopting when trying to move."); throw new IllegalStateException("Should not be ota-dexopting when trying to move."); } } Loading