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

Commit b2558071 authored by Noah Zimmt's avatar Noah Zimmt
Browse files

Avoid calling installd where not needed

The method StorageStatsManager#getCacheBytes is called several times
during package installation. It calls
InstalldNativeService::getUserSize via a binder call.

The runtime of getUserSize is variable; I have traces where it takes
50ms and traces where it takes 500ms.

The return value of getUserSize used to take into account freeable cache
space when determining available space on a given volume. In most cases,
it is possible to first do a pessimistic check of the space available on
the volume - forgoing the installd call - and only consider cache space
if the pessimistic check fails.

This avoids a binder call + the time spend in getUserSize for most
installs.

Bug: None
Test: atest frameworks/base/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
cts/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
Change-Id: Icaecee732ef330fee1b409d2dd76723822c25959
parent 44a27da9
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -1987,11 +1987,31 @@ public class StorageManager {
     */
    public static final int FLAG_ALLOCATE_DEFY_HALF_RESERVED = 1 << 2;

    /**
     * Flag indicating that a disk space check should not take into account
     * freeable cached space when determining allocatable space.
     *
     * Intended for use with {@link #getAllocatableBytes()}.
     * @hide
     */
    public static final int FLAG_ALLOCATE_NON_CACHE_ONLY = 1 << 3;

    /**
     * Flag indicating that a disk space check should only return freeable
     * cached space when determining allocatable space.
     *
     * Intended for use with {@link #getAllocatableBytes()}.
     * @hide
     */
    public static final int FLAG_ALLOCATE_CACHE_ONLY = 1 << 4;

    /** @hide */
    @IntDef(flag = true, prefix = { "FLAG_ALLOCATE_" }, value = {
            FLAG_ALLOCATE_AGGRESSIVE,
            FLAG_ALLOCATE_DEFY_ALL_RESERVED,
            FLAG_ALLOCATE_DEFY_HALF_RESERVED,
            FLAG_ALLOCATE_NON_CACHE_ONLY,
            FLAG_ALLOCATE_CACHE_ONLY,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface AllocateFlags {}
+15 −2
Original line number Diff line number Diff line
@@ -261,8 +261,21 @@ public class PackageHelper {
    public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException {
        final StorageManager storage = context.getSystemService(StorageManager.class);
        final UUID target = storage.getUuidForPath(Environment.getDataDirectory());
        return (params.sizeBytes <= storage.getAllocatableBytes(target,
                translateAllocateFlags(params.installFlags)));
        final int flags = translateAllocateFlags(params.installFlags);

        final long allocateableBytes = storage.getAllocatableBytes(target,
                flags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY);

        // If we fit on internal storage without including freeable cache space, don't bother
        // checking to determine how much space is taken up by the cache.
        if (params.sizeBytes <= allocateableBytes) {
            return true;
        }

        final long cacheClearable = storage.getAllocatableBytes(target,
                flags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY);

        return params.sizeBytes <= allocateableBytes + cacheClearable;
    }

    public static boolean fitsOnExternal(Context context, SessionParams params) {
+27 −20
Original line number Diff line number Diff line
@@ -3208,29 +3208,29 @@ class StorageManagerService extends IStorageManager.Stub
            // should be kept in sync with getFreeBytes().
            final File path = storage.findPathForUuid(volumeUuid);

            final long usable = path.getUsableSpace();
            final long lowReserved = storage.getStorageLowBytes(path);
            final long fullReserved = storage.getStorageFullBytes(path);
            long usable = 0;
            long lowReserved = 0;
            long fullReserved = 0;
            long cacheClearable = 0;

            if (stats.isQuotaSupported(volumeUuid)) {
            if ((flags & StorageManager.FLAG_ALLOCATE_CACHE_ONLY) == 0) {
                usable = path.getUsableSpace();
                lowReserved = storage.getStorageLowBytes(path);
                fullReserved = storage.getStorageFullBytes(path);
            }

            if ((flags & StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY) == 0
                    && stats.isQuotaSupported(volumeUuid)) {
                final long cacheTotal = stats.getCacheBytes(volumeUuid);
                final long cacheReserved = storage.getStorageCacheBytes(path, flags);
                final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
                cacheClearable = Math.max(0, cacheTotal - cacheReserved);
            }

            if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
                return Math.max(0, (usable + cacheClearable) - fullReserved);
            } else {
                return Math.max(0, (usable + cacheClearable) - lowReserved);
            }
            } else {
                // When we don't have fast quota information, we ignore cached
                // data and only consider unused bytes.
                if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
                    return Math.max(0, usable - fullReserved);
                } else {
                    return Math.max(0, usable - lowReserved);
                }
            }
        } catch (IOException e) {
            throw new ParcelableException(e);
        } finally {
@@ -3242,10 +3242,17 @@ class StorageManagerService extends IStorageManager.Stub
    public void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) {
        flags = adjustAllocateFlags(flags, Binder.getCallingUid(), callingPackage);

        final long allocatableBytes = getAllocatableBytes(volumeUuid, flags, callingPackage);
        final long allocatableBytes = getAllocatableBytes(volumeUuid,
                flags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY, callingPackage);
        if (bytes > allocatableBytes) {
            // If we don't have room without taking cache into account, check to see if we'd have
            // room if we included freeable cache space.
            final long cacheClearable = getAllocatableBytes(volumeUuid,
                    flags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY, callingPackage);
            if (bytes > allocatableBytes + cacheClearable) {
                throw new ParcelableException(new IOException("Failed to allocate " + bytes
                    + " because only " + allocatableBytes + " allocatable"));
                    + " because only " + (allocatableBytes + cacheClearable) + " allocatable"));
            }
        }

        final StorageManager storage = mContext.getSystemService(StorageManager.class);