Create separate api for reconciling per-sdk storage
Sdks should get their own storage area under the sdk package path
at: `/data/misc_ce/0/sdksandbox/<package>/<sdk-name>@<random-suffix>`
The random suffix is to prevent sdks from guessing each others storage
paths. It is separated by '@' which is an illegal character for package
names.
The new API prepares the sdk-level directories as follows:
- creates them if they do not exist.
- on occasssions where they already exist, it skips creating them.
- on occasssions where it finds an per-sdk directory that shouldn't be
there, it deletes them.
- it also creates the package path if its missing for any reason.
Additional Changes:
- For package-level sdk data directory, we now delete it if FLAG_STORAGE_SDK
flag is not passed. This is done so that we clean up properly when an
app that consumes sdk updates to a state where it no longer consumes
sdks.
Bug: 211763739
Test: atest installd_service_tes
Test: atest SdkSandboxStorageHostTest
Ignore-AOSP-First: Feature is being developed in internal branch
Change-Id: I7f144f5da4c4e10851f78d731de5dfb429fbac4d
Merged-In: I7f144f5da4c4e10851f78d731de5dfb429fbac4d
(cherry picked from commit 92f5cf0d0b943315d475dfbe9273a6118095bdbf)
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 2705505..be56f83 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -41,6 +41,7 @@
#include <fstream>
#include <functional>
#include <regex>
+#include <unordered_set>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -81,6 +82,7 @@
// #define GRANULAR_LOCKS
using android::base::ParseUint;
+using android::base::Split;
using android::base::StringPrintf;
using std::endl;
@@ -723,21 +725,26 @@
}
if (flags & FLAG_STORAGE_SDK) {
- auto status = createSdkSandboxDataDirectory(uuid, packageName, userId, appId, previousAppId,
- seInfo, flags);
- if (!status.isOk()) {
- return status;
+ // Safe to ignore status since we can retry creating this by calling reconcileSdkData
+ auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId,
+ previousAppId, seInfo, flags);
+ if (!ignore.isOk()) {
+ PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName;
}
+
+ } else {
+ // Package does not need sdk storage. Remove it.
+ deleteSdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
}
return ok();
}
/**
- * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<app-name> directory and other
+ * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<package-name> directory and other
* app level sub directories, such as ./shared
*/
-binder::Status InstalldNativeService::createSdkSandboxDataDirectory(
+binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory(
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t flags) {
int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
@@ -746,7 +753,6 @@
return ok();
}
- // TODO(b/211763739): what if uuid is not nullptr or TEST?
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
@@ -761,10 +767,14 @@
// during user creation
// Prepare the app directory
- auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
- packageName.c_str());
- if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) {
- return error("Failed to prepare " + appPath);
+ auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+ packageName.c_str());
+#if SDK_DEBUG
+ LOG(DEBUG) << "Creating app-level sdk data directory: " << packagePath;
+#endif
+
+ if (prepare_app_dir(packagePath, 0751, AID_SYSTEM)) {
+ return error("Failed to prepare " + packagePath);
}
// Now prepare the shared directory which will be accessible by all codes
@@ -792,6 +802,32 @@
return ok();
}
+/**
+ * Responsible for deleting /data/misc_{ce|de}/user/0/sdksandbox/<package-name> directory
+ */
+binder::Status InstalldNativeService::deleteSdkSandboxDataPackageDirectory(
+ const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+ int32_t flags) {
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ auto res = ok();
+
+ constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int currentFlag : storageFlags) {
+ if ((flags & currentFlag) == 0) {
+ continue;
+ }
+ bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+
+ const auto& packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+ packageName.c_str());
+ if (delete_dir_contents_and_dir(packagePath, /*ignore_if_missing=*/true) != 0) {
+ res = error("Failed to delete sdk package directory: " + packagePath);
+ }
+ }
+
+ return res;
+}
+
binder::Status InstalldNativeService::createAppData(
const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
@@ -835,6 +871,140 @@
return ok();
}
+binder::Status InstalldNativeService::reconcileSdkData(
+ const android::os::ReconcileSdkDataArgs& args) {
+ ENFORCE_UID(AID_SYSTEM);
+ // Locking is performed depeer in the callstack.
+
+ return reconcileSdkData(args.uuid, args.packageName, args.sdkPackageNames, args.randomSuffixes,
+ args.userId, args.appId, args.previousAppId, args.seInfo, args.flags);
+}
+
+/**
+ * Reconciles per-sdk directory under app-level sdk data directory.
+
+ * E.g. `/data/misc_ce/0/sdksandbox/<package-name>/<sdkPackageName>-<randomSuffix>
+ *
+ * - If the sdk data package directory is missing, we create it first.
+ * - If sdkPackageNames is empty, we delete sdk package directory since it's not needed anymore.
+ * - If a sdk level directory we need to prepare already exist, we skip creating it again. This
+ * is to avoid having same per-sdk directory with different suffix.
+ * - If a sdk level directory exist which is absent from sdkPackageNames, we remove it.
+ */
+binder::Status InstalldNativeService::reconcileSdkData(
+ const std::optional<std::string>& uuid, const std::string& packageName,
+ const std::vector<std::string>& sdkPackageNames,
+ const std::vector<std::string>& randomSuffixes, int userId, int appId, int previousAppId,
+ const std::string& seInfo, int flags) {
+ CHECK_ARGUMENT_UUID(uuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ for (const auto& sdkPackageName : sdkPackageNames) {
+ CHECK_ARGUMENT_PACKAGE_NAME(sdkPackageName);
+ }
+ LOCK_PACKAGE_USER();
+
+#if SDK_DEBUG
+ LOG(DEBUG) << "Creating per sdk data directory for: " << packageName;
+#endif
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ // Validate we have enough randomSuffixStrings
+ if (randomSuffixes.size() != sdkPackageNames.size()) {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ StringPrintf("Not enough random suffix. Required %d, received %d.",
+ (int)sdkPackageNames.size(), (int)randomSuffixes.size()));
+ }
+
+ // Prepare the sdk package directory in case it's missing
+ const auto status = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId,
+ previousAppId, seInfo, flags);
+ if (!status.isOk()) {
+ return status;
+ }
+
+ auto res = ok();
+ // We have to create sdk data for CE and DE storage
+ const int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+ for (int currentFlag : storageFlags) {
+ if ((flags & currentFlag) == 0) {
+ continue;
+ }
+ const bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+
+ // Since random suffix provided will be random every time, we need to ensure we don't end up
+ // creating multuple directories for same sdk package with different suffixes. This
+ // is ensured by fetching all the existing sub directories and storing them so that we can
+ // check for existence later. We also remove unconsumed sdk directories in this check.
+ const auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
+ packageName.c_str());
+ const std::unordered_set<std::string> expectedSdkNames(sdkPackageNames.begin(),
+ sdkPackageNames.end());
+ // Store paths of per-sdk directory for sdk that already exists
+ std::unordered_map<std::string, std::string> sdkNamesThatExist;
+
+ const auto subDirHandler = [&packagePath, &expectedSdkNames, &sdkNamesThatExist,
+ &res](const std::string& filename) {
+ auto filepath = packagePath + "/" + filename;
+ auto tokens = Split(filename, "@");
+ if (tokens.size() != 2) {
+ // Not a per-sdk directory with random suffix
+ return;
+ }
+ auto sdkName = tokens[0];
+
+ // Remove the per-sdk directory if it is not referred in
+ // expectedSdkNames
+ if (expectedSdkNames.find(sdkName) == expectedSdkNames.end()) {
+ if (delete_dir_contents_and_dir(filepath) != 0) {
+ res = error("Failed to delete " + filepath);
+ return;
+ }
+ } else {
+ // Otherwise, store it as existing sdk level directory
+ sdkNamesThatExist[sdkName] = filepath;
+ }
+ };
+ const int ec = foreach_subdir(packagePath, subDirHandler);
+ if (ec != 0) {
+ res = error("Failed to process subdirs for " + packagePath);
+ continue;
+ }
+
+ // Create sdksandbox data directory for each sdksandbox package
+ for (int i = 0, size = sdkPackageNames.size(); i < size; i++) {
+ const std::string& sdkName = sdkPackageNames[i];
+ const std::string& randomSuffix = randomSuffixes[i];
+ std::string path;
+ if (const auto& it = sdkNamesThatExist.find(sdkName); it != sdkNamesThatExist.end()) {
+ // Already exists. Use existing path instead of creating a new one
+ path = it->second;
+ } else {
+ path = create_data_misc_sdk_sandbox_sdk_path(uuid_, isCeData, userId,
+ packageName.c_str(), sdkName.c_str(),
+ randomSuffix.c_str());
+ }
+
+ // Create the directory along with cache and code_cache
+ const int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
+ if (cacheGid == -1) {
+ return exception(binder::Status::EX_ILLEGAL_STATE,
+ StringPrintf("cacheGid cannot be -1 for sdk data"));
+ }
+ const int32_t sandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
+ int32_t previousSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId);
+ auto status = createAppDataDirs(path, sandboxUid, &previousSandboxUid, cacheGid, seInfo,
+ 0700);
+ if (!status.isOk()) {
+ res = status;
+ continue;
+ }
+ }
+ }
+
+ return res;
+}
+
binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);