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

Commit 26b068e9 authored by Martijn Coenen's avatar Martijn Coenen Committed by Gerrit Code Review
Browse files

Merge "Implement quota calculation for project ID based quota."

parents 7f9edd9b 771cc344
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -75,8 +75,7 @@ void CacheTracker::loadStats() {

bool CacheTracker::loadQuotaStats() {
    int cacheGid = multiuser_get_cache_gid(mUserId, mAppId);
    int extCacheGid = multiuser_get_ext_cache_gid(mUserId, mAppId);
    if (IsQuotaSupported(mUuid) && cacheGid != -1 && extCacheGid != -1) {
    if (IsQuotaSupported(mUuid) && cacheGid != -1) {
        int64_t space;
        if ((space = GetOccupiedSpaceForGid(mUuid, cacheGid)) != -1) {
            cacheUsed += space;
@@ -84,7 +83,7 @@ bool CacheTracker::loadQuotaStats() {
            return false;
        }

        if ((space = GetOccupiedSpaceForGid(mUuid, extCacheGid)) != -1) {
        if ((space = get_occupied_app_cache_space_external(mUuid, mUserId, mAppId)) != -1) {
            cacheUsed += space;
        } else {
            return false;
+144 −46
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@
#include <log/log.h>               // TODO: Move everything to base/logging.
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
#include <private/android_projectid_config.h>
#include <selinux/android.h>
#include <system/thread_defs.h>
#include <utils/Trace.h>
@@ -1477,8 +1478,8 @@ static std::string toString(std::vector<int64_t> values) {
static void collectQuotaStats(const std::string& uuid, int32_t userId,
        int32_t appId, struct stats* stats, struct stats* extStats) {
    int64_t space;
    if (stats != nullptr) {
    uid_t uid = multiuser_get_uid(userId, appId);
    if (stats != nullptr) {
        if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) {
            stats->dataSize += space;
        }
@@ -1499,20 +1500,44 @@ static void collectQuotaStats(const std::string& uuid, int32_t userId,
    }

    if (extStats != nullptr) {
        int extGid = multiuser_get_ext_gid(userId, appId);
        if (extGid != -1) {
            if ((space = GetOccupiedSpaceForGid(uuid, extGid)) != -1) {
        static const bool supportsSdCardFs = supports_sdcardfs();
        space = get_occupied_app_space_external(uuid, userId, appId);

        if (space != -1) {
            extStats->dataSize += space;
            if (!supportsSdCardFs && stats != nullptr) {
                // On devices without sdcardfs, if internal and external are on
                // the same volume, a uid such as u0_a123 is used for
                // application dirs on both internal and external storage;
                // therefore, substract that amount from internal to make sure
                // we don't count it double.
                stats->dataSize -= space;
            }
        }

        int extCacheGid = multiuser_get_ext_cache_gid(userId, appId);
        if (extCacheGid != -1) {
            if ((space = GetOccupiedSpaceForGid(uuid, extCacheGid)) != -1) {
                extStats->dataSize += space;
        space = get_occupied_app_cache_space_external(uuid, userId, appId);
        if (space != -1) {
            extStats->dataSize += space; // cache counts for "data"
            extStats->cacheSize += space;
            if (!supportsSdCardFs && stats != nullptr) {
                // On devices without sdcardfs, if internal and external are on
                // the same volume, a uid such as u0_a123 is used for both
                // internal and external storage; therefore, substract that
                // amount from internal to make sure we don't count it double.
                stats->dataSize -= space;
            }
        }

        if (!supportsSdCardFs && stats != nullptr) {
            // On devices without sdcardfs, the UID of OBBs on external storage
            // matches the regular app UID (eg u0_a123); therefore, to avoid
            // OBBs being include in stats->dataSize, compute the OBB size for
            // this app, and substract it from the size reported on internal
            // storage
            long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START;
            int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId);
            stats->dataSize -= appObbSize;
        }
    }
}

@@ -1761,6 +1786,106 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri
    return ok();
}

struct external_sizes {
    int64_t audioSize;
    int64_t videoSize;
    int64_t imageSize;
    int64_t totalSize; // excludes OBBs (Android/obb), but includes app data + cache
    int64_t obbSize;
};

#define PER_USER_RANGE 100000

static long getProjectIdForUser(int userId, long projectId) {
    return userId * PER_USER_RANGE + projectId;
}

static external_sizes getExternalSizesForUserWithQuota(const std::string& uuid, int32_t userId, const std::vector<int32_t>& appIds) {
    struct external_sizes sizes = {};
    int64_t space;

    if (supports_sdcardfs()) {
        uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
        if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) {
            sizes.totalSize = space;
        }

        gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO);
        if ((space = GetOccupiedSpaceForGid(uuid, audioGid)) != -1) {
            sizes.audioSize = space;
        }

        gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO);
        if ((space = GetOccupiedSpaceForGid(uuid, videoGid)) != -1) {
            sizes.videoSize = space;
        }

        gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE);
        if ((space = GetOccupiedSpaceForGid(uuid, imageGid)) != -1) {
            sizes.imageSize = space;
        }

        if ((space = GetOccupiedSpaceForGid(uuid, AID_MEDIA_OBB)) != -1) {
            sizes.obbSize = space;
        }
    } else {
        int64_t totalSize = 0;
        long defaultProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_DEFAULT);
        if ((space = GetOccupiedSpaceForProjectId(uuid, defaultProjectId)) != -1) {
            // This is all files that are not audio/video/images, excluding
            // OBBs and app-private data
            totalSize += space;
        }

        long audioProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_AUDIO);
        if ((space = GetOccupiedSpaceForProjectId(uuid, audioProjectId)) != -1) {
            sizes.audioSize = space;
            totalSize += space;
        }

        long videoProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_VIDEO);
        if ((space = GetOccupiedSpaceForProjectId(uuid, videoProjectId)) != -1) {
            sizes.videoSize = space;
            totalSize += space;
        }

        long imageProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_IMAGE);
        if ((space = GetOccupiedSpaceForProjectId(uuid, imageProjectId)) != -1) {
            sizes.imageSize = space;
            totalSize += space;
        }

        int64_t totalAppDataSize = 0;
        int64_t totalAppCacheSize = 0;
        int64_t totalAppObbSize = 0;
        for (auto appId : appIds) {
            if (appId >= AID_APP_START) {
                // App data
                uid_t uid = multiuser_get_uid(userId, appId);
                long projectId = uid - AID_APP_START + PROJECT_ID_EXT_DATA_START;
                totalAppDataSize += GetOccupiedSpaceForProjectId(uuid, projectId);

                // App cache
                long cacheProjectId = uid - AID_APP_START + PROJECT_ID_EXT_CACHE_START;
                totalAppCacheSize += GetOccupiedSpaceForProjectId(uuid, cacheProjectId);

                // App OBBs
                long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START;
                totalAppObbSize += GetOccupiedSpaceForProjectId(uuid, obbProjectId);
            }
        }
        // Total size should include app data + cache
        totalSize += totalAppDataSize;
        totalSize += totalAppCacheSize;
        sizes.totalSize = totalSize;

        // Only OBB is separate
        sizes.obbSize = totalAppObbSize;
    }

    return sizes;
}

binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
        int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
        std::vector<int64_t>* _aidl_return) {
@@ -1789,14 +1914,6 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str
    }

    if (flags & FLAG_USE_QUOTA) {
        int64_t space;

        ATRACE_BEGIN("obb");
        if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) {
            extStats.codeSize += space;
        }
        ATRACE_END();

        ATRACE_BEGIN("code");
        calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true);
        ATRACE_END();
@@ -1818,10 +1935,9 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str
        }

        ATRACE_BEGIN("external");
        uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
        if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) {
            extStats.dataSize += space;
        }
        auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds);
        extStats.dataSize += sizes.totalSize;
        extStats.codeSize += sizes.obbSize;
        ATRACE_END();

        if (!uuid) {
@@ -1832,13 +1948,11 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str
                    -1, -1, true);
            ATRACE_END();
        }

        ATRACE_BEGIN("quota");
        int64_t dataSize = extStats.dataSize;
        for (auto appId : appIds) {
            if (appId >= AID_APP_START) {
                collectQuotaStats(uuidString, userId, appId, &stats, &extStats);

#if MEASURE_DEBUG
                // Sleep to make sure we don't lose logs
                usleep(1);
@@ -1934,29 +2048,13 @@ binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std:
    }

    if (flags & FLAG_USE_QUOTA) {
        int64_t space;

        ATRACE_BEGIN("quota");
        uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
        if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) {
            totalSize = space;
        }

        gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO);
        if ((space = GetOccupiedSpaceForGid(uuidString, audioGid)) != -1) {
            audioSize = space;
        }
        gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO);
        if ((space = GetOccupiedSpaceForGid(uuidString, videoGid)) != -1) {
            videoSize = space;
        }
        gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE);
        if ((space = GetOccupiedSpaceForGid(uuidString, imageGid)) != -1) {
            imageSize = space;
        }
        if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) {
            obbSize = space;
        }
        auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds);
        totalSize = sizes.totalSize;
        audioSize = sizes.audioSize;
        videoSize = sizes.videoSize;
        imageSize = sizes.imageSize;
        obbSize = sizes.obbSize;
        ATRACE_END();

        ATRACE_BEGIN("apps");
+48 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <sys/xattr.h>
#include <sys/statvfs.h>

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
@@ -34,9 +35,11 @@
#include <cutils/properties.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <private/android_projectid_config.h>

#include "dexopt_return_codes.h"
#include "globals.h"  // extern variables.
#include "QuotaUtils.h"

#ifndef LOG_TAG
#define LOG_TAG "installd"
@@ -1060,6 +1063,51 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta
    return 0;
}

static const char* kProcFilesystems = "/proc/filesystems";
bool supports_sdcardfs() {
    std::string supported;
    if (!android::base::ReadFileToString(kProcFilesystems, &supported)) {
        PLOG(ERROR) << "Failed to read supported filesystems";
        return false;
    }
    return supported.find("sdcardfs\n") != std::string::npos;
}

int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId) {
    static const bool supportsSdcardFs = supports_sdcardfs();

    if (supportsSdcardFs) {
        int extGid = multiuser_get_ext_gid(userId, appId);

        if (extGid == -1) {
            return -1;
        }

        return GetOccupiedSpaceForGid(uuid, extGid);
    } else {
        uid_t uid = multiuser_get_uid(userId, appId);
        long projectId = uid - AID_APP_START + PROJECT_ID_EXT_DATA_START;
        return GetOccupiedSpaceForProjectId(uuid, projectId);
    }
}
int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId) {
    static const bool supportsSdcardFs = supports_sdcardfs();

    if (supportsSdcardFs) {
        int extCacheGid = multiuser_get_ext_cache_gid(userId, appId);

        if (extCacheGid == -1) {
            return -1;
        }

        return GetOccupiedSpaceForGid(uuid, extCacheGid);
    } else {
        uid_t uid = multiuser_get_uid(userId, appId);
        long projectId = uid - AID_APP_START + PROJECT_ID_EXT_CACHE_START;
        return GetOccupiedSpaceForProjectId(uuid, projectId);
    }
}

// Collect all non empty profiles from the given directory and puts then into profile_paths.
// The profiles are identified based on PROFILE_EXT extension.
// If a subdirectory or profile file cannot be opened the method logs a warning and moves on.
+4 −0
Original line number Diff line number Diff line
@@ -150,6 +150,10 @@ int wait_child(pid_t pid);
int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
        uid_t uid, gid_t gid);

bool supports_sdcardfs();
int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId);
int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId);

// Collect all non empty profiles from the global profile directory and
// put then into profile_paths. The profiles are identified based on PROFILE_EXT extension.
// If a subdirectory or profile file cannot be opened the method logs a warning and moves on.