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

Commit f0631bdb authored by Noah Zimmt's avatar Noah Zimmt Committed by Android (Google) Code Review
Browse files

Merge "Reduce calls to installd when resolving volume"

parents 92bd4506 e42bc52d
Loading
Loading
Loading
Loading
+60 −27
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
@@ -166,6 +166,23 @@ public class PackageHelper {
                params.sizeBytes, testableInterface);
    }

    private static boolean checkFitOnVolume(StorageManager storageManager, String volumePath,
            SessionParams params) throws IOException {
        if (volumePath == null) {
            return false;
        }
        final int installFlags = translateAllocateFlags(params.installFlags);
        final UUID target = storageManager.getUuidForPath(new File(volumePath));
        final long availBytes = storageManager.getAllocatableBytes(target,
                installFlags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY);
        if (params.sizeBytes <= availBytes) {
            return true;
        }
        final long cacheClearable = storageManager.getAllocatableBytes(target,
                installFlags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY);
        return params.sizeBytes <= availBytes + cacheClearable;
    }

    @VisibleForTesting
    public static String resolveInstallVolume(Context context, SessionParams params,
            TestableInterface testInterface) throws IOException {
@@ -178,35 +195,23 @@ public class PackageHelper {
        ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context,
                params.appPackageName);

        // Figure out best candidate volume, and also if we fit on internal
        final ArraySet<String> allCandidates = new ArraySet<>();
        boolean fitsOnInternal = false;
        VolumeInfo bestCandidate = null;
        long bestCandidateAvailBytes = Long.MIN_VALUE;
        final ArrayMap<String, String> volumePaths = new ArrayMap<>();
        String internalVolumePath = null;
        for (VolumeInfo vol : storageManager.getVolumes()) {
            if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
                final boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
                final UUID target = storageManager.getUuidForPath(new File(vol.path));
                final long availBytes = storageManager.getAllocatableBytes(target,
                        translateAllocateFlags(params.installFlags));
                if (isInternalStorage) {
                    fitsOnInternal = (params.sizeBytes <= availBytes);
                    internalVolumePath = vol.path;
                }
                if (!isInternalStorage || allow3rdPartyOnInternal) {
                    if (availBytes >= params.sizeBytes) {
                        allCandidates.add(vol.fsUuid);
                    }
                    if (availBytes >= bestCandidateAvailBytes) {
                        bestCandidate = vol;
                        bestCandidateAvailBytes = availBytes;
                    }
                    volumePaths.put(vol.fsUuid, vol.path);
                }
            }
        }

        // System apps always forced to internal storage
        if (existingInfo != null && existingInfo.isSystemApp()) {
            if (fitsOnInternal) {
            if (checkFitOnVolume(storageManager, internalVolumePath, params)) {
                return StorageManager.UUID_PRIVATE_INTERNAL;
            } else {
                throw new IOException("Not enough space on existing volume "
@@ -228,7 +233,7 @@ public class PackageHelper {
                throw new IOException("Not allowed to install non-system apps on internal storage");
            }

            if (fitsOnInternal) {
            if (checkFitOnVolume(storageManager, internalVolumePath, params)) {
                return StorageManager.UUID_PRIVATE_INTERNAL;
            } else {
                throw new IOException("Requested internal only, but not enough space");
@@ -237,10 +242,14 @@ public class PackageHelper {

        // If app already exists somewhere, we must stay on that volume
        if (existingInfo != null) {
            if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)
                    && fitsOnInternal) {
                return StorageManager.UUID_PRIVATE_INTERNAL;
            } else if (allCandidates.contains(existingInfo.volumeUuid)) {
            String existingVolumePath = null;
            if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
                existingVolumePath = internalVolumePath;
            } else if (volumePaths.containsKey(existingInfo.volumeUuid)) {
                existingVolumePath = volumePaths.get(existingInfo.volumeUuid);
            }

            if (checkFitOnVolume(storageManager, existingVolumePath, params)) {
                return existingInfo.volumeUuid;
            } else {
                throw new IOException("Not enough space on existing volume "
@@ -250,13 +259,37 @@ public class PackageHelper {

        // We're left with new installations with either preferring external or auto, so just pick
        // volume with most space
        if (bestCandidate != null) {
            return bestCandidate.fsUuid;
        if (volumePaths.size() == 1) {
            if (checkFitOnVolume(storageManager, volumePaths.valueAt(0), params)) {
                return volumePaths.keyAt(0);
            }
        } else {
            String bestCandidate = null;
            long bestCandidateAvailBytes = Long.MIN_VALUE;
            for (String vol : volumePaths.keySet()) {
                final String volumePath = volumePaths.get(vol);
                final UUID target = storageManager.getUuidForPath(new File(volumePath));

                // We need to take into account freeable cached space, because we're choosing the
                // best candidate amongst a list, not just checking if we fit at all.
                final long availBytes = storageManager.getAllocatableBytes(target,
                        translateAllocateFlags(params.installFlags));

                if (availBytes >= bestCandidateAvailBytes) {
                    bestCandidate = vol;
                    bestCandidateAvailBytes = availBytes;
                }
            }

            if (bestCandidateAvailBytes >= params.sizeBytes) {
                return bestCandidate;
            }

        }

        throw new IOException("No special requests, but no room on allowed volumes. "
                + " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal);
    }
    }

    public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException {
        final StorageManager storage = context.getSystemService(StorageManager.class);