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/tests/Android.bp b/cmds/installd/tests/Android.bp
index e390bab..2e63dd6 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -26,6 +26,7 @@
         "libasync_safe",
         "libdiskusage",
         "libext2_uuid",
+        "libgmock",
         "libinstalld",
         "liblog",
     ],
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 31f5dce..30a23eb 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -36,6 +36,7 @@
 #include <android/content/pm/IPackageManagerNative.h>
 #include <binder/IServiceManager.h>
 #include "InstalldNativeService.h"
+#include "binder/Status.h"
 #include "binder_test_utils.h"
 #include "dexopt.h"
 #include "globals.h"
@@ -965,10 +966,10 @@
     bool exists(const char* path) { return ::access(path, F_OK) == 0; }
 
     // Creates a default CreateAppDataArgs object
-    android::os::CreateAppDataArgs createAppDataArgs() {
+    android::os::CreateAppDataArgs createAppDataArgs(std::string packageName) {
         android::os::CreateAppDataArgs args;
         args.uuid = kTestUuid;
-        args.packageName = "com.foo";
+        args.packageName = packageName;
         args.userId = kTestUserId;
         args.appId = kTestAppId;
         args.seInfo = "default";
@@ -976,6 +977,26 @@
         return args;
     }
 
+    android::os::ReconcileSdkDataArgs reconcileSdkDataArgs(
+            std::string packageName, std::vector<std::string> codeNames,
+            std::vector<std::string> randomSuffixes) {
+        android::os::ReconcileSdkDataArgs args;
+        args.uuid = kTestUuid;
+        args.packageName = packageName;
+        for (const auto& codeName : codeNames) {
+            args.sdkPackageNames.push_back(codeName);
+        }
+        for (const auto& randomSuffix : randomSuffixes) {
+            args.randomSuffixes.push_back(randomSuffix);
+        }
+        args.userId = kTestUserId;
+        args.appId = kTestAppId;
+        args.previousAppId = -1;
+        args.seInfo = "default";
+        args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+        return args;
+    }
+
 protected:
     InstalldNativeService* service;
 
@@ -1000,41 +1021,37 @@
 
 private:
     void clearAppData() {
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user", true));
         ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
         ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_ce", true));
         ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_de", true));
-        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
     }
 };
 
 TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData) {
     android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs();
-    args.packageName = "com.foo";
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
 
     // Create the app user data.
     ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
 
-    CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
-    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700);
-    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid,
-                    S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid,
-                    S_IFDIR | S_ISGID | 0771);
+    const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo";
+    CheckFileAccess(fooCePath, kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess(fooCePath + "/shared", kTestSdkSandboxUid, S_IFDIR | 0700);
+    CheckFileAccess(fooCePath + "/shared/cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(fooCePath + "/shared/code_cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
 
-    CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
-    CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700);
-    CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid,
-                    S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid,
-                    S_IFDIR | S_ISGID | 0771);
+    const std::string fooDePath = "misc_de/0/sdksandbox/com.foo";
+    CheckFileAccess(fooDePath, kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess(fooDePath + "/shared", kTestSdkSandboxUid, S_IFDIR | 0700);
+    CheckFileAccess(fooDePath + "/shared/cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(fooDePath + "/shared/code_cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
 }
 
-TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutSdkFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlag) {
     android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs();
-    args.packageName = "com.foo";
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
 
     // Create the app user data.
@@ -1044,10 +1061,24 @@
     ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
 }
 
-TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutDeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlagDeletesExisting) {
     android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs();
-    args.packageName = "com.foo";
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+
+    args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_SDK;
 
     // Create the app user data.
@@ -1060,10 +1091,9 @@
     ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
 }
 
-TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutCeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutCeFlag) {
     android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs();
-    args.packageName = "com.foo";
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
 
     // Create the app user data.
@@ -1076,5 +1106,96 @@
     CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
 }
 
+TEST_F(SdkSandboxDataTest, ReconcileSdkData) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+    // Create the sdk data.
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    const std::string barCePath = "misc_ce/0/sdksandbox/com.foo/bar@random1";
+    CheckFileAccess(barCePath, kTestSdkSandboxUid, S_IFDIR | 0700);
+    CheckFileAccess(barCePath + "/cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(barCePath + "/code_cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+
+    const std::string bazCePath = "misc_ce/0/sdksandbox/com.foo/baz@random2";
+    CheckFileAccess(bazCePath, kTestSdkSandboxUid, S_IFDIR | 0700);
+    CheckFileAccess(bazCePath + "/cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(bazCePath + "/code_cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+
+    const std::string barDePath = "misc_de/0/sdksandbox/com.foo/bar@random1";
+    CheckFileAccess(barDePath, kTestSdkSandboxUid, S_IFDIR | 0700);
+    CheckFileAccess(barDePath + "/cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(barDePath + "/code_cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+
+    const std::string bazDePath = "misc_de/0/sdksandbox/com.foo/baz@random2";
+    CheckFileAccess(bazDePath, kTestSdkSandboxUid, S_IFDIR | 0700);
+    CheckFileAccess(bazDePath + "/cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(bazDePath + "/code_cache", kTestSdkSandboxUid, S_IFDIR | S_ISGID | 0771);
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_PackageNameCannotUseRandomSuffixSeparator) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar@illegal"}, {"random1"});
+
+    // Create the sdksandbox data.
+    auto status = service->reconcileSdkData(args);
+    ASSERT_EQ(status.exceptionCode(), binder::Status::EX_ILLEGAL_ARGUMENT);
+    ASSERT_EQ(status.exceptionMessage(), "Package name bar@illegal is malformed");
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_NotEnoughRandomSuffix) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1"});
+
+    // Create the sdksandbox data.
+    auto status = service->reconcileSdkData(args);
+    ASSERT_EQ(status.exceptionCode(), binder::Status::EX_ILLEGAL_ARGUMENT);
+    ASSERT_EQ(status.exceptionMessage(), "Not enough random suffix. Required 2, received 1.");
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_DirectoryNotCreatedIfAlreadyExistsIgnoringSuffix) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+    // Create the sdksandbox data.
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // Retry with different random suffix
+    args.randomSuffixes[0] = "r10";
+    args.randomSuffixes[1] = "r20";
+
+    // Create the sdksandbox data again
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // Previous directories from first attempt should exist
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar@random1", kTestSdkSandboxUid, S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, S_IFDIR | 0700);
+    // No new directories should be created on second attempt
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@r10"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo/bar@r20"));
+}
+
+TEST_F(SdkSandboxDataTest, ReconcileSdkData_ExtraCodeDirectoriesAreDeleted) {
+    android::os::ReconcileSdkDataArgs args =
+            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+
+    // Create the sdksandbox data.
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // Retry with different package name
+    args.sdkPackageNames[0] = "bar.diff";
+
+    // Create the sdksandbox data again
+    ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
+
+    // New directoris should exist
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar.diff@random1", kTestSdkSandboxUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, S_IFDIR | 0700);
+    // Directory for old unreferred sdksandbox package name should be removed
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@random1"));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 17802a3..38c1c05 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -21,6 +21,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/scopeguard.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "InstalldNativeService.h"
@@ -47,6 +48,8 @@
 namespace android {
 namespace installd {
 
+using ::testing::UnorderedElementsAre;
+
 class UtilsTest : public testing::Test {
 protected:
     virtual void SetUp() {
@@ -658,6 +661,23 @@
     ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700));
 }
 
+TEST_F(UtilsTest, TestForEachSubdir) {
+    auto deleter = [&]() {
+        delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */);
+    };
+    auto scope_guard = android::base::make_scope_guard(deleter);
+
+    system("mkdir -p /data/local/tmp/user/0/com.foo");
+    system("mkdir -p /data/local/tmp/user/0/com.bar");
+    system("touch /data/local/tmp/user/0/some-file");
+
+    std::vector<std::string> result;
+    foreach_subdir("/data/local/tmp/user/0",
+                   [&](const std::string &filename) { result.push_back(filename); });
+
+    EXPECT_THAT(result, UnorderedElementsAre("com.foo", "com.bar"));
+}
+
 TEST_F(UtilsTest, TestSdkSandboxDataPaths) {
     // Ce data paths
     EXPECT_EQ("/data/misc_ce/0/sdksandbox",
@@ -673,6 +693,8 @@
               create_data_misc_sdk_sandbox_shared_path(nullptr, true, 0, "com.foo"));
     EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared",
               create_data_misc_sdk_sandbox_shared_path(nullptr, true, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/bar@random",
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "bar", "random"));
 
     // De data paths
     EXPECT_EQ("/data/misc_de/0/sdksandbox",
@@ -688,6 +710,9 @@
               create_data_misc_sdk_sandbox_shared_path(nullptr, false, 0, "com.foo"));
     EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared",
               create_data_misc_sdk_sandbox_shared_path(nullptr, false, 10, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/bar@random",
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "bar",
+                                                    "random"));
 }
 
 TEST_F(UtilsTest, WaitChild) {