Implement quota calculation for project ID based quota.

Devices launching with R will no longer have the sdcardfs filesystem;
instead, quota tracking on external storage is implemented by project
IDs. Switch over the existing quota calculation to use project IDs when
sdcardfs is not available.

Bug: 146419093
Test: atest StorageHostTest (on devices with and without sdcardfs)
Change-Id: I17925c811b08c7c85fff02ee6e279e4d7586e3ff
This commit is contained in:
Martijn Coenen 2020-02-19 23:26:56 +01:00
parent 55c0da3208
commit 771cc344c6
4 changed files with 198 additions and 49 deletions

View file

@ -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;

View file

@ -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>
@ -1474,8 +1475,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;
uid_t uid = multiuser_get_uid(userId, appId);
if (stats != nullptr) {
uid_t uid = multiuser_get_uid(userId, appId);
if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) {
stats->dataSize += space;
}
@ -1496,20 +1497,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) {
extStats->dataSize += space;
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;
extStats->cacheSize += 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;
}
}
}
@ -1758,6 +1783,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) {
@ -1786,14 +1911,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();
@ -1815,10 +1932,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) {
@ -1829,13 +1945,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);
@ -1931,29 +2045,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");

View file

@ -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.

View file

@ -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.