Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b332aa4 authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

Merge "OtaDexoptService: Run dexopt on all apps am: 14186731 am: 4775ba50"...

Merge "OtaDexoptService: Run dexopt on all apps am: 14186731 am: 4775ba50" into nyc-dev-plus-aosp
parents 885368f4 be1adf2e
Loading
Loading
Loading
Loading
+64 −125
Original line number Diff line number Diff line
@@ -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;

@@ -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
@@ -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();
@@ -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);
@@ -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.");
        }