Merge "SurfaceFlinger: Use a lockless stack for binder->tracing thread" into tm-dev
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 176c84d..6d3a2fe 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;
 
@@ -456,8 +458,8 @@
     return res;
 }
 
-static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
-    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid) {
+    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
         PLOG(ERROR) << "Failed to prepare " << path;
         return -1;
     }
@@ -597,9 +599,9 @@
     }
 }
 
-static binder::Status createAppDataDirs(const std::string& path,
-        int32_t uid, int32_t* previousUid, int32_t cacheGid,
-        const std::string& seInfo, mode_t targetMode) {
+static binder::Status createAppDataDirs(const std::string& path, int32_t uid, int32_t gid,
+                                        int32_t* previousUid, int32_t cacheGid,
+                                        const std::string& seInfo, mode_t targetMode) {
     struct stat st{};
     bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
 
@@ -623,9 +625,9 @@
     }
 
     // Prepare only the parent app directory
-    if (prepare_app_dir(path, targetMode, uid) ||
-            prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
-            prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+    if (prepare_app_dir(path, targetMode, uid, gid) ||
+        prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+        prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
         return error("Failed to prepare " + path);
     }
 
@@ -684,7 +686,7 @@
     if (flags & FLAG_STORAGE_CE) {
         auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
 
-        auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+        auto status = createAppDataDirs(path, uid, uid, &previousUid, cacheGid, seInfo, targetMode);
         if (!status.isOk()) {
             return status;
         }
@@ -709,7 +711,7 @@
     if (flags & FLAG_STORAGE_DE) {
         auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
 
-        auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+        auto status = createAppDataDirs(path, uid, uid, &previousUid, cacheGid, seInfo, targetMode);
         if (!status.isOk()) {
             return status;
         }
@@ -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.
+        destroySdkSandboxDataPackageDirectory(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, AID_SYSTEM)) {
+            return error("Failed to prepare " + packagePath);
         }
 
         // Now prepare the shared directory which will be accessible by all codes
@@ -777,8 +787,8 @@
             return exception(binder::Status::EX_ILLEGAL_STATE,
                              StringPrintf("cacheGid cannot be -1 for sdksandbox data"));
         }
-        auto status = createAppDataDirs(sharedPath, sdkSandboxUid, &previousSdkSandboxUid, cacheGid,
-                                        seInfo, 0700);
+        auto status = createAppDataDirs(sharedPath, sdkSandboxUid, AID_NOBODY,
+                                        &previousSdkSandboxUid, cacheGid, seInfo, 0700);
         if (!status.isOk()) {
             return status;
         }
@@ -835,6 +845,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, AID_NOBODY, &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);
@@ -980,6 +1124,47 @@
             }
         }
     }
+    auto status = clearSdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
+    if (!status.isOk()) {
+        res = status;
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags) {
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int i = 0; i < 2; i++) {
+        int currentFlag = storageFlags[i];
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+        std::string suffix;
+        if (flags & FLAG_CLEAR_CACHE_ONLY) {
+            suffix = CACHE_DIR_POSTFIX;
+        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+            suffix = CODE_CACHE_DIR_POSTFIX;
+        }
+
+        auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname);
+        if (access(appPath.c_str(), F_OK) != 0) continue;
+        const auto subDirHandler = [&appPath, &res, &suffix](const std::string& filename) {
+            auto filepath = appPath + "/" + filename + suffix;
+            if (delete_dir_contents(filepath, true) != 0) {
+                res = error("Failed to clear contents of " + filepath);
+            }
+        };
+        const int ec = foreach_subdir(appPath, subDirHandler);
+        if (ec != 0) {
+            res = error("Failed to process subdirs for " + appPath);
+        }
+    }
     return res;
 }
 
@@ -1076,6 +1261,32 @@
             }
         }
     }
+    auto status = destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags);
+    if (!status.isOk()) {
+        res = status;
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t flags) {
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int i = 0; i < 2; i++) {
+        int currentFlag = storageFlags[i];
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+        auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname);
+        if (rename_delete_dir_contents_and_dir(appPath) != 0) {
+            res = error("Failed to delete " + appPath);
+        }
+    }
     return res;
 }
 
@@ -3269,11 +3480,17 @@
     if (flags & FLAG_STORAGE_CE) {
         auto ce_path = create_data_user_ce_path(uuid_cstr, userId);
         cleanup_invalid_package_dirs_under_path(ce_path);
+        auto sdksandbox_ce_path =
+                create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/true, userId);
+        cleanup_invalid_package_dirs_under_path(sdksandbox_ce_path);
     }
 
     if (flags & FLAG_STORAGE_DE) {
         auto de_path = create_data_user_de_path(uuid_cstr, userId);
         cleanup_invalid_package_dirs_under_path(de_path);
+        auto sdksandbox_de_path =
+                create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/false, userId);
+        cleanup_invalid_package_dirs_under_path(sdksandbox_de_path);
     }
 
     return ok();
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index c9a4d50..55b0511 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -58,6 +58,8 @@
             const std::vector<android::os::CreateAppDataArgs>& args,
             std::vector<android::os::CreateAppDataResult>* _aidl_return);
 
+    binder::Status reconcileSdkData(const android::os::ReconcileSdkDataArgs& args);
+
     binder::Status restoreconAppData(const std::optional<std::string>& uuid,
             const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
             const std::string& seInfo);
@@ -205,10 +207,23 @@
                                        const std::string& seInfo, int32_t targetSdkVersion,
                                        int64_t* _aidl_return);
 
-    binder::Status createSdkSandboxDataDirectory(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);
+    binder::Status reconcileSdkData(const std::optional<std::string>& uuid,
+                                    const std::string& packageName,
+                                    const std::vector<std::string>& sdkPackageNames,
+                                    const std::vector<std::string>& randomSuffixes, int32_t userId,
+                                    int32_t appId, int32_t previousAppId, const std::string& seInfo,
+                                    int flags);
+    binder::Status 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);
+    binder::Status clearSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid,
+                                                       const std::string& packageName,
+                                                       int32_t userId, int32_t flags);
+    binder::Status destroySdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid,
+                                                         const std::string& packageName,
+                                                         int32_t userId, int32_t flags);
 };
 
 }  // namespace installd
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 428e2b3..e08e9b6 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -24,6 +24,8 @@
     android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
     android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
 
+    void reconcileSdkData(in android.os.ReconcileSdkDataArgs args);
+
     void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
             int userId, int flags, int appId, @utf8InCpp String seInfo);
     void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
new file mode 100644
index 0000000..2f794b1
--- /dev/null
+++ b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable ReconcileSdkDataArgs {
+    @nullable @utf8InCpp String uuid;
+    @utf8InCpp String packageName;
+    @utf8InCpp List<String> sdkPackageNames;
+    @utf8InCpp List<String> randomSuffixes;
+    int userId;
+    int appId;
+    int previousAppId;
+    @utf8InCpp String seInfo;
+    int flags;
+}
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index e390bab..b3baca5 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",
     ],
@@ -106,6 +107,7 @@
         "libziparchive",
         "liblog",
         "liblogwrap",
+        "libc++fs",
     ],
     test_config: "installd_service_test.xml",
 
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 31f5dce..4423045 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -32,16 +32,20 @@
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
+#include <filesystem>
+#include <fstream>
 
 #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"
 #include "utils.h"
 
 using android::base::StringPrintf;
+namespace fs = std::filesystem;
 
 namespace android {
 std::string get_package_name(uid_t uid) {
@@ -75,13 +79,15 @@
 namespace installd {
 
 static constexpr const char* kTestUuid = "TEST";
-static constexpr const char* kTestPath = "/data/local/tmp/user/0";
+static constexpr const char* kTestPath = "/data/local/tmp";
+static constexpr const uid_t kNobodyUid = 9999;
 static constexpr const uid_t kSystemUid = 1000;
 static constexpr const int32_t kTestUserId = 0;
 static constexpr const uid_t kTestAppId = 19999;
 static constexpr const int FLAG_STORAGE_SDK = InstalldNativeService::FLAG_STORAGE_SDK;
 
 const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
+const gid_t kTestCacheGid = multiuser_get_cache_gid(kTestUserId, kTestAppId);
 const uid_t kTestSdkSandboxUid = multiuser_get_sdk_sandbox_uid(kTestUserId, kTestAppId);
 
 #define FLAG_FORCE InstalldNativeService::FLAG_FORCE
@@ -104,18 +110,18 @@
     return create_cache_path_default(path, src, instruction_set);
 }
 
-static std::string get_full_path(const char* path) {
-    return StringPrintf("%s/%s", kTestPath, path);
+static std::string get_full_path(const std::string& path) {
+    return StringPrintf("%s/%s", kTestPath, path.c_str());
 }
 
-static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
     const std::string fullPath = get_full_path(path);
     EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0);
     EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0);
     EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
 }
 
-static int create(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
     int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
     EXPECT_NE(fd, -1);
     EXPECT_EQ(::fchown(fd, owner, group), 0);
@@ -123,8 +129,8 @@
     return fd;
 }
 
-static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
-    EXPECT_EQ(::close(create(path, owner, group, mode)), 0);
+static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
+    EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0);
 }
 
 static int stat_gid(const char* path) {
@@ -139,7 +145,7 @@
     return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
 }
 
-static bool exists(const char* path) {
+static bool exists(const std::string& path) {
     return ::access(get_full_path(path).c_str(), F_OK) == 0;
 }
 
@@ -162,10 +168,11 @@
     return result;
 }
 
-static bool exists_renamed_deleted_dir() {
-    return find_file(kTestPath, [](const std::string& name, bool is_dir) {
-        return is_dir && is_renamed_deleted_dir(name);
-    });
+static bool exists_renamed_deleted_dir(const std::string& rootDirectory) {
+    return find_file((std::string(kTestPath) + rootDirectory).c_str(),
+                     [](const std::string& name, bool is_dir) {
+                         return is_dir && is_renamed_deleted_dir(name);
+                     });
 }
 
 class ServiceTest : public testing::Test {
@@ -180,197 +187,205 @@
         service = new InstalldNativeService();
         testUuid = kTestUuid;
         system("rm -rf /data/local/tmp/user");
+        system("rm -rf /data/local/tmp/misc_ce");
+        system("rm -rf /data/local/tmp/misc_de");
         system("mkdir -p /data/local/tmp/user/0");
-
+        system("mkdir -p /data/local/tmp/misc_ce/0/sdksandbox");
+        system("mkdir -p /data/local/tmp/misc_de/0/sdksandbox");
         init_globals_from_data_and_root();
     }
 
     virtual void TearDown() {
         delete service;
         system("rm -rf /data/local/tmp/user");
+        system("rm -rf /data/local/tmp/misc_ce");
+        system("rm -rf /data/local/tmp/misc_de");
     }
 };
 
 TEST_F(ServiceTest, FixupAppData_Upgrade) {
     LOG(INFO) << "FixupAppData_Upgrade";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/normal", 10000, 10000, 0700);
-    mkdir("com.example/cache", 10000, 10000, 0700);
-    touch("com.example/cache/file", 10000, 10000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/normal", 10000, 10000, 0700);
+    mkdir("user/0/com.example/cache", 10000, 10000, 0700);
+    touch("user/0/com.example/cache/file", 10000, 10000, 0700);
 
     service->fixupAppData(testUuid, 0);
 
-    EXPECT_EQ(10000, stat_gid("com.example/normal"));
-    EXPECT_EQ(20000, stat_gid("com.example/cache"));
-    EXPECT_EQ(20000, stat_gid("com.example/cache/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/normal"));
+    EXPECT_EQ(20000, stat_gid("user/0/com.example/cache"));
+    EXPECT_EQ(20000, stat_gid("user/0/com.example/cache/file"));
 
-    EXPECT_EQ(0700, stat_mode("com.example/normal"));
-    EXPECT_EQ(02771, stat_mode("com.example/cache"));
-    EXPECT_EQ(0700, stat_mode("com.example/cache/file"));
+    EXPECT_EQ(0700, stat_mode("user/0/com.example/normal"));
+    EXPECT_EQ(02771, stat_mode("user/0/com.example/cache"));
+    EXPECT_EQ(0700, stat_mode("user/0/com.example/cache/file"));
 }
 
 TEST_F(ServiceTest, FixupAppData_Moved) {
     LOG(INFO) << "FixupAppData_Moved";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+    touch("user/0/com.example/bar/file", 10000, 20000, 0700);
 
     service->fixupAppData(testUuid, 0);
 
-    EXPECT_EQ(10000, stat_gid("com.example/foo"));
-    EXPECT_EQ(20000, stat_gid("com.example/foo/file"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/foo"));
+    EXPECT_EQ(20000, stat_gid("user/0/com.example/foo/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file"));
 
     service->fixupAppData(testUuid, FLAG_FORCE);
 
-    EXPECT_EQ(10000, stat_gid("com.example/foo"));
-    EXPECT_EQ(10000, stat_gid("com.example/foo/file"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar"));
-    EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/foo"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/foo/file"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar"));
+    EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file"));
 }
 
 TEST_F(ServiceTest, DestroyUserData) {
     LOG(INFO) << "DestroyUserData";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+    touch("user/0/com.example/bar/file", 10000, 20000, 0700);
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+    EXPECT_TRUE(exists("user/0/com.example/foo"));
+    EXPECT_TRUE(exists("user/0/com.example/foo/file"));
+    EXPECT_TRUE(exists("user/0/com.example/bar"));
+    EXPECT_TRUE(exists("user/0/com.example/bar/file"));
 
     service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE);
 
-    EXPECT_FALSE(exists("com.example/foo"));
-    EXPECT_FALSE(exists("com.example/foo/file"));
-    EXPECT_FALSE(exists("com.example/bar"));
-    EXPECT_FALSE(exists("com.example/bar/file"));
+    EXPECT_FALSE(exists("user/0/com.example/foo"));
+    EXPECT_FALSE(exists("user/0/com.example/foo/file"));
+    EXPECT_FALSE(exists("user/0/com.example/bar"));
+    EXPECT_FALSE(exists("user/0/com.example/bar/file"));
 
-    EXPECT_FALSE(exists_renamed_deleted_dir());
+    EXPECT_FALSE(exists_renamed_deleted_dir("/user/0"));
 }
 
 TEST_F(ServiceTest, DestroyAppData) {
     LOG(INFO) << "DestroyAppData";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example/bar", 10000, 20000, 0700);
+    touch("user/0/com.example/bar/file", 10000, 20000, 0700);
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+    EXPECT_TRUE(exists("user/0/com.example/foo"));
+    EXPECT_TRUE(exists("user/0/com.example/foo/file"));
+    EXPECT_TRUE(exists("user/0/com.example/bar"));
+    EXPECT_TRUE(exists("user/0/com.example/bar/file"));
 
     service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0);
 
-    EXPECT_FALSE(exists("com.example/foo"));
-    EXPECT_FALSE(exists("com.example/foo/file"));
-    EXPECT_FALSE(exists("com.example/bar"));
-    EXPECT_FALSE(exists("com.example/bar/file"));
+    EXPECT_FALSE(exists("user/0/com.example/foo"));
+    EXPECT_FALSE(exists("user/0/com.example/foo/file"));
+    EXPECT_FALSE(exists("user/0/com.example/bar"));
+    EXPECT_FALSE(exists("user/0/com.example/bar/file"));
 
-    EXPECT_FALSE(exists_renamed_deleted_dir());
+    EXPECT_FALSE(exists_renamed_deleted_dir("/user/0"));
 }
 
 TEST_F(ServiceTest, CleanupInvalidPackageDirs) {
     LOG(INFO) << "CleanupInvalidPackageDirs";
 
-    mkdir("5b14b6458a44==deleted==", 10000, 10000, 0700);
-    mkdir("5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
-    touch("5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
-    mkdir("5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
-    touch("5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
+    std::string rootDirectoryPrefix[] = {"user/0", "misc_ce/0/sdksandbox", "misc_de/0/sdksandbox"};
+    for (auto& prefix : rootDirectoryPrefix) {
+        mkdir(prefix + "/5b14b6458a44==deleted==", 10000, 10000, 0700);
+        mkdir(prefix + "/5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
+        touch(prefix + "/5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
+        touch(prefix + "/5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
 
-    auto fd = create("5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
+        auto fd = create(prefix + "/5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
 
-    mkdir("b14b6458a44NOTdeleted", 10000, 10000, 0700);
-    mkdir("b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
-    touch("b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
-    mkdir("b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
-    touch("b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
+        mkdir(prefix + "/b14b6458a44NOTdeleted", 10000, 10000, 0700);
+        mkdir(prefix + "/b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
+        touch(prefix + "/b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
+        touch(prefix + "/b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
-    mkdir("com.example/bar", 10000, 20000, 0700);
-    touch("com.example/bar/file", 10000, 20000, 0700);
+        mkdir(prefix + "/com.example", 10000, 10000, 0700);
+        mkdir(prefix + "/com.example/foo", 10000, 10000, 0700);
+        touch(prefix + "/com.example/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/com.example/bar", 10000, 20000, 0700);
+        touch(prefix + "/com.example/bar/file", 10000, 20000, 0700);
 
-    mkdir("==deleted==", 10000, 10000, 0700);
-    mkdir("==deleted==/foo", 10000, 10000, 0700);
-    touch("==deleted==/foo/file", 10000, 20000, 0700);
-    mkdir("==deleted==/bar", 10000, 20000, 0700);
-    touch("==deleted==/bar/file", 10000, 20000, 0700);
+        mkdir(prefix + "/==deleted==", 10000, 10000, 0700);
+        mkdir(prefix + "/==deleted==/foo", 10000, 10000, 0700);
+        touch(prefix + "/==deleted==/foo/file", 10000, 20000, 0700);
+        mkdir(prefix + "/==deleted==/bar", 10000, 20000, 0700);
+        touch(prefix + "/==deleted==/bar/file", 10000, 20000, 0700);
 
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo/file"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/file"));
-    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file"));
 
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file"));
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar/file"));
 
-    EXPECT_TRUE(exists("==deleted==/foo"));
-    EXPECT_TRUE(exists("==deleted==/foo/file"));
-    EXPECT_TRUE(exists("==deleted==/bar"));
-    EXPECT_TRUE(exists("==deleted==/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/foo"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/bar"));
+        EXPECT_TRUE(exists(prefix + "/==deleted==/bar/file"));
 
-    EXPECT_TRUE(exists_renamed_deleted_dir());
+        EXPECT_TRUE(exists_renamed_deleted_dir("/" + prefix));
 
-    service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
+        service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
 
-    EXPECT_EQ(::close(fd), 0);
+        EXPECT_EQ(::close(fd), 0);
 
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo/file"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/file"));
-    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo/file"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/file"));
+        EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file"));
 
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
-    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar"));
+        EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file"));
 
-    EXPECT_TRUE(exists("com.example/foo"));
-    EXPECT_TRUE(exists("com.example/foo/file"));
-    EXPECT_TRUE(exists("com.example/bar"));
-    EXPECT_TRUE(exists("com.example/bar/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo"));
+        EXPECT_TRUE(exists(prefix + "/com.example/foo/file"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar"));
+        EXPECT_TRUE(exists(prefix + "/com.example/bar/file"));
 
-    EXPECT_FALSE(exists("==deleted==/foo"));
-    EXPECT_FALSE(exists("==deleted==/foo/file"));
-    EXPECT_FALSE(exists("==deleted==/bar"));
-    EXPECT_FALSE(exists("==deleted==/bar/file"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/foo"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/foo/file"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/bar"));
+        EXPECT_FALSE(exists(prefix + "/==deleted==/bar/file"));
 
-    EXPECT_FALSE(exists_renamed_deleted_dir());
+        EXPECT_FALSE(exists_renamed_deleted_dir(prefix));
+    }
 }
 
 TEST_F(ServiceTest, HashSecondaryDex) {
     LOG(INFO) << "HashSecondaryDex";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
         dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
@@ -390,7 +405,7 @@
     LOG(INFO) << "HashSecondaryDex_NoSuch";
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
         dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
@@ -400,12 +415,12 @@
 TEST_F(ServiceTest, HashSecondaryDex_Unreadable) {
     LOG(INFO) << "HashSecondaryDex_Unreadable";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0300);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0300);
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
         dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
 
@@ -415,12 +430,12 @@
 TEST_F(ServiceTest, HashSecondaryDex_WrongApp) {
     LOG(INFO) << "HashSecondaryDex_WrongApp";
 
-    mkdir("com.example", 10000, 10000, 0700);
-    mkdir("com.example/foo", 10000, 10000, 0700);
-    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("user/0/com.example", 10000, 10000, 0700);
+    mkdir("user/0/com.example/foo", 10000, 10000, 0700);
+    touch("user/0/com.example/foo/file", 10000, 20000, 0700);
 
     std::vector<uint8_t> result;
-    std::string dexPath = get_full_path("com.example/foo/file");
+    std::string dexPath = get_full_path("user/0/com.example/foo/file");
     EXPECT_BINDER_FAIL(service->hashSecondaryDexFile(
         dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result));
 }
@@ -952,27 +967,47 @@
 
 class SdkSandboxDataTest : public testing::Test {
 public:
-    void CheckFileAccess(const std::string& path, uid_t uid, mode_t mode) {
+    void CheckFileAccess(const std::string& path, uid_t uid, gid_t gid, mode_t mode) {
         const auto fullPath = "/data/local/tmp/" + path;
         ASSERT_TRUE(exists(fullPath.c_str())) << "For path: " << fullPath;
         struct stat st;
         ASSERT_EQ(0, stat(fullPath.c_str(), &st));
         ASSERT_EQ(uid, st.st_uid) << "For path: " << fullPath;
-        ASSERT_EQ(uid, st.st_gid) << "For path: " << fullPath;
+        ASSERT_EQ(gid, st.st_gid) << "For path: " << fullPath;
         ASSERT_EQ(mode, st.st_mode) << "For path: " << fullPath;
     }
 
     bool exists(const char* path) { return ::access(path, F_OK) == 0; }
 
     // Creates a default CreateAppDataArgs object
-    android::os::CreateAppDataArgs createAppDataArgs() {
+    android::os::CreateAppDataArgs createAppDataArgs(const 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";
-        args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
+        args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+        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;
     }
 
@@ -1000,41 +1035,41 @@
 
 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) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData) {
     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,
+    const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo";
+    CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess(fooCePath + "/shared", kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(fooCePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
                     S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid,
+    CheckFileAccess(fooCePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
                     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,
+    const std::string fooDePath = "misc_de/0/sdksandbox/com.foo";
+    CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess(fooDePath + "/shared", kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(fooDePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
                     S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid,
+    CheckFileAccess(fooDePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
                     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,26 +1079,39 @@
     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.
     ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
 
     // Only CE paths should exist
-    CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, kSystemUid, S_IFDIR | 0751);
 
     // DE paths should not exist
     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.
@@ -1073,7 +1121,290 @@
     ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
 
     // Only DE paths should exist
-    CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, 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, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(barCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(barCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    const std::string bazCePath = "misc_ce/0/sdksandbox/com.foo/baz@random2";
+    CheckFileAccess(bazCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(bazCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(bazCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    const std::string barDePath = "misc_de/0/sdksandbox/com.foo/bar@random1";
+    CheckFileAccess(barDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(barDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(barDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    const std::string bazDePath = "misc_de/0/sdksandbox/com.foo/baz@random2";
+    CheckFileAccess(bazDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | 0700);
+    CheckFileAccess(bazDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess(bazDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid,
+                    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, kNobodyUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid,
+                    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, kNobodyUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid,
+                    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"));
+}
+
+class DestroyAppDataTest : public SdkSandboxDataTest {};
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithCeAndDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    // Destroy the app user data.
+    ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+                                                  args.flags, result.ceDataInode));
+    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(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    // Destroy the app user data.
+    ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+                                                  FLAG_STORAGE_CE, result.ceDataInode));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+}
+
+TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutCeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    // Destroy the app user data.
+    ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId,
+                                                  FLAG_STORAGE_DE, result.ceDataInode));
+    ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo"));
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
+}
+
+class ClearAppDataTest : public SdkSandboxDataTest {
+public:
+    void createTestSdkData(const std::string& packageName, std::vector<std::string> sdkNames) {
+        const auto& cePackagePath = "/data/local/tmp/misc_ce/0/sdksandbox/" + packageName;
+        const auto& dePackagePath = "/data/local/tmp/misc_de/0/sdksandbox/" + packageName;
+        ASSERT_TRUE(mkdirs(cePackagePath + "/shared/cache", 0700));
+        ASSERT_TRUE(mkdirs(cePackagePath + "shared/code_cache", 0700));
+        ASSERT_TRUE(mkdirs(dePackagePath + "/shared/cache", 0700));
+        ASSERT_TRUE(mkdirs(dePackagePath + "/shared/code_cache", 0700));
+        std::ofstream{cePackagePath + "/shared/cache/cachedTestData.txt"};
+        for (auto sdkName : sdkNames) {
+            ASSERT_TRUE(mkdirs(cePackagePath + "/" + sdkName + "/cache", 0700));
+            ASSERT_TRUE(mkdirs(dePackagePath + "/" + sdkName + "/cache", 0700));
+            ASSERT_TRUE(mkdirs(cePackagePath + "/" + sdkName + "/code_cache", 0700));
+            ASSERT_TRUE(mkdirs(dePackagePath + "/" + sdkName + "/code_cache", 0700));
+            std::ofstream{cePackagePath + "/" + sdkName + "/cache/cachedTestData.txt"};
+            std::ofstream{cePackagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"};
+            std::ofstream{dePackagePath + "/" + sdkName + "/cache/cachedTestData.txt"};
+            std::ofstream{dePackagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"};
+        }
+    }
+};
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_CE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2/cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCodeCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_CE |
+                                          (InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(
+            fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2/code_cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_DE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1/cache")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2/cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCodeCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(
+            service->clearAppData(args.uuid, args.packageName, args.userId,
+                                  FLAG_STORAGE_DE |
+                                          (InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY),
+                                  result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(
+            fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1/code_cache")));
+    ASSERT_TRUE(
+            fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2/code_cache")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndWithoutAnyCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(service->clearAppData(args.uuid, args.packageName, args.userId,
+                                                FLAG_STORAGE_CE, result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/shared")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk1")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/sdk2")));
+}
+
+TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndWithoutAnyCacheFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
+    args.packageName = "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));
+    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    // Clear the app user data.
+    ASSERT_BINDER_SUCCESS(service->clearAppData(args.uuid, args.packageName, args.userId,
+                                                FLAG_STORAGE_DE, result.ceDataInode));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/shared")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk1")));
+    ASSERT_TRUE(fs::is_empty(fs::path("/data/local/tmp/misc_de/0/sdksandbox/com.foo/sdk2")));
 }
 
 }  // namespace installd
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) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 992425d..8cfd123 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -212,7 +212,7 @@
 
 /**
  * Create the path name where code data for all codes in a particular app will be stored.
- * E.g. /data/misc_ce/0/sdksandbox/<app-name>
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>
  */
 std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData,
                                                       userid_t user, const char* package_name) {
@@ -224,7 +224,7 @@
 
 /**
  * Create the path name where shared code data for a particular app will be stored.
- * E.g. /data/misc_ce/0/sdksandbox/<app-name>/shared
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>/shared
  */
 std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData,
                                                      userid_t user, const char* package_name) {
@@ -234,6 +234,19 @@
                                 .c_str());
 }
 
+/**
+ * Create the path name where per-code level data for a particular app will be stored.
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>/<sdk-name>-<random-suffix>
+ */
+std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData,
+                                                  userid_t user, const char* package_name,
+                                                  const char* sdk_name, const char* randomSuffix) {
+    check_package_name(sdk_name);
+    auto package_path =
+            create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user, package_name);
+    return StringPrintf("%s/%s@%s", package_path.c_str(), sdk_name, randomSuffix);
+}
+
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) {
     return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
 }
@@ -696,6 +709,34 @@
     return std::unique_ptr<DIR, DirCloser>(::opendir(dir));
 }
 
+// Collects filename of subdirectories of given directory and passes it to the function
+int foreach_subdir(const std::string& pathname, const std::function<void(const std::string&)> fn) {
+    auto dir = open_dir(pathname.c_str());
+    if (!dir) return -1;
+
+    int dfd = dirfd(dir.get());
+    if (dfd < 0) {
+        ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno));
+        return -1;
+    }
+
+    struct dirent* de;
+    while ((de = readdir(dir.get()))) {
+        if (de->d_type != DT_DIR) {
+            continue;
+        }
+
+        std::string name{de->d_name};
+        // always skip "." and ".."
+        if (name == "." || name == "..") {
+            continue;
+        }
+        fn(name);
+    }
+
+    return 0;
+}
+
 void cleanup_invalid_package_dirs_under_path(const std::string& pathname) {
     auto dir = open_dir(pathname.c_str());
     if (!dir) {
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 4b56f99..54d77f9 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -32,6 +32,7 @@
 
 #define MEASURE_DEBUG 0
 #define FIXUP_DEBUG 0
+#define SDK_DEBUG 1
 
 #define BYPASS_QUOTA 0
 #define BYPASS_SDCARDFS 0
@@ -66,6 +67,9 @@
                                                       userid_t userid, const char* package_name);
 std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData,
                                                      userid_t userid, const char* package_name);
+std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData,
+                                                  userid_t userid, const char* package_name,
+                                                  const char* sdk_name, const char* randomSuffix);
 
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user);
 std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user);
@@ -130,6 +134,8 @@
 bool is_renamed_deleted_dir(const std::string& path);
 int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = true);
 
+int foreach_subdir(const std::string& pathname, std::function<void(const std::string&)> fn);
+
 void cleanup_invalid_package_dirs_under_path(const std::string& pathname);
 
 int delete_dir_contents(const char *pathname,
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7448308..63d87da 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -166,6 +166,7 @@
         "-Wextra-semi",
         "-Werror",
         "-Wzero-as-null-pointer-constant",
+        "-Wreorder-init-list",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 0970ca5..01b25d3 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -19,6 +19,7 @@
 #include <atomic>
 #include <set>
 
+#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
@@ -281,9 +282,11 @@
             err = pingBinder();
             break;
         case EXTENSION_TRANSACTION:
+            CHECK(reply != nullptr);
             err = reply->writeStrongBinder(getExtension());
             break;
         case DEBUG_PID_TRANSACTION:
+            CHECK(reply != nullptr);
             err = reply->writeInt32(getDebugPid());
             break;
         case SET_RPC_CLIENT_TRANSACTION: {
@@ -590,6 +593,7 @@
 {
     switch (code) {
         case INTERFACE_TRANSACTION:
+            CHECK(reply != nullptr);
             reply->writeString16(getInterfaceDescriptor());
             return NO_ERROR;
 
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 056ef0a..921e57c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -72,29 +72,29 @@
     e.cleanupCookie = cleanupCookie;
     e.func = func;
 
-    if (ssize_t idx = mObjects.indexOfKey(objectID); idx >= 0) {
+    if (mObjects.find(objectID) != mObjects.end()) {
         ALOGI("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object "
               "ID already in use",
               objectID, this, object);
-        return mObjects[idx].object;
+        return mObjects[objectID].object;
     }
 
-    mObjects.add(objectID, e);
+    mObjects.insert({objectID, e});
     return nullptr;
 }
 
 void* BpBinder::ObjectManager::find(const void* objectID) const
 {
-    const ssize_t i = mObjects.indexOfKey(objectID);
-    if (i < 0) return nullptr;
-    return mObjects.valueAt(i).object;
+    auto i = mObjects.find(objectID);
+    if (i == mObjects.end()) return nullptr;
+    return i->second.object;
 }
 
 void* BpBinder::ObjectManager::detach(const void* objectID) {
-    ssize_t idx = mObjects.indexOfKey(objectID);
-    if (idx < 0) return nullptr;
-    void* value = mObjects[idx].object;
-    mObjects.removeItemsAt(idx, 1);
+    auto i = mObjects.find(objectID);
+    if (i == mObjects.end()) return nullptr;
+    void* value = i->second.object;
+    mObjects.erase(i);
     return value;
 }
 
@@ -102,10 +102,10 @@
 {
     const size_t N = mObjects.size();
     ALOGV("Killing %zu objects in manager %p", N, this);
-    for (size_t i=0; i<N; i++) {
-        const entry_t& e = mObjects.valueAt(i);
+    for (auto i : mObjects) {
+        const entry_t& e = i.second;
         if (e.func != nullptr) {
-            e.func(mObjects.keyAt(i), e.object, e.cleanupCookie);
+            e.func(i.first, e.object, e.cleanupCookie);
         }
     }
 
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index bd974b0..9c7ff97 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -31,9 +31,10 @@
 #include <binder/Parcel.h>
 #include <log/log.h>
 
-#include <utils/KeyedVector.h>
 #include <utils/threads.h>
 
+#include <map>
+
 #define VERBOSE   0
 
 namespace android {
@@ -63,7 +64,7 @@
     void free_heap(const wp<IBinder>& binder);
 
     Mutex mHeapCacheLock;  // Protects entire vector below.
-    KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
+    std::map<wp<IBinder>, heap_info_t> mHeapCache;
     // We do not use the copy-on-write capabilities of KeyedVector.
     // TODO: Reimplemement based on standard C++ container?
 };
@@ -434,9 +435,9 @@
 sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
 {
     Mutex::Autolock _l(mHeapCacheLock);
-    ssize_t i = mHeapCache.indexOfKey(binder);
-    if (i>=0) {
-        heap_info_t& info = mHeapCache.editValueAt(i);
+    auto i = mHeapCache.find(binder);
+    if (i != mHeapCache.end()) {
+        heap_info_t& info = i->second;
         ALOGD_IF(VERBOSE,
                 "found binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
                 binder.get(), info.heap.get(),
@@ -452,7 +453,7 @@
         info.count = 1;
         //ALOGD("adding binder=%p, heap=%p, count=%d",
         //      binder.get(), info.heap.get(), info.count);
-        mHeapCache.add(binder, info);
+        mHeapCache.insert({binder, info});
         return info.heap;
     }
 }
@@ -466,9 +467,9 @@
     sp<IMemoryHeap> rel;
     {
         Mutex::Autolock _l(mHeapCacheLock);
-        ssize_t i = mHeapCache.indexOfKey(binder);
-        if (i>=0) {
-            heap_info_t& info(mHeapCache.editValueAt(i));
+        auto i = mHeapCache.find(binder);
+        if (i != mHeapCache.end()) {
+            heap_info_t& info = i->second;
             if (--info.count == 0) {
                 ALOGD_IF(VERBOSE,
                         "removing binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
@@ -477,8 +478,8 @@
                         static_cast<BpMemoryHeap*>(info.heap.get())
                             ->mHeapId.load(memory_order_relaxed),
                         info.count);
-                rel = mHeapCache.valueAt(i).heap;
-                mHeapCache.removeItemsAt(i);
+                rel = i->second.heap;
+                mHeapCache.erase(i);
             }
         } else {
             ALOGE("free_heap binder=%p not found!!!", binder.unsafe_get());
@@ -490,23 +491,23 @@
 {
     sp<IMemoryHeap> realHeap;
     Mutex::Autolock _l(mHeapCacheLock);
-    ssize_t i = mHeapCache.indexOfKey(binder);
-    if (i>=0)   realHeap = mHeapCache.valueAt(i).heap;
-    else        realHeap = interface_cast<IMemoryHeap>(binder);
+    auto i = mHeapCache.find(binder);
+    if (i != mHeapCache.end())
+        realHeap = i->second.heap;
+    else
+        realHeap = interface_cast<IMemoryHeap>(binder);
     return realHeap;
 }
 
 void HeapCache::dump_heaps()
 {
     Mutex::Autolock _l(mHeapCacheLock);
-    int c = mHeapCache.size();
-    for (int i=0 ; i<c ; i++) {
-        const heap_info_t& info = mHeapCache.valueAt(i);
+    for (const auto& i : mHeapCache) {
+        const heap_info_t& info = i.second;
         BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(info.heap.get()));
-        ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)",
-                mHeapCache.keyAt(i).unsafe_get(),
-                info.heap.get(), info.count,
-                h->mHeapId.load(memory_order_relaxed), h->mBase, h->mSize);
+        ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)", i.first.unsafe_get(),
+              info.heap.get(), info.count, h->mHeapId.load(memory_order_relaxed), h->mBase,
+              h->mSize);
     }
 }
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 13f0a4c..f79075d 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1199,7 +1199,8 @@
 
     case BR_DECREFS:
         refs = (RefBase::weakref_type*)mIn.readPointer();
-        obj = (BBinder*)mIn.readPointer();
+        // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
+        obj = (BBinder*)mIn.readPointer(); // consume
         // NOTE: This assertion is not valid, because the object may no
         // longer exist (thus the (BBinder*)cast above resulting in a different
         // memory address).
@@ -1409,7 +1410,7 @@
                                               uint32_t *async_received)
 {
     int ret = 0;
-    binder_frozen_status_info info;
+    binder_frozen_status_info info = {};
     info.pid = pid;
 
 #if defined(__ANDROID__)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 6a138e3..504c6c2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -63,7 +63,7 @@
 // This macro should never be used at runtime, as a too large value
 // of s could cause an integer overflow. Instead, you should always
 // use the wrapper function pad_size()
-#define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
+#define PAD_SIZE_UNSAFE(s) (((s) + 3) & ~3UL)
 
 static size_t pad_size(size_t s) {
     if (s > (std::numeric_limits<size_t>::max() - 3)) {
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index b84395e..e79cb86 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -152,8 +152,13 @@
 }
 
 status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
-    return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
-        // std::move'd from fd becomes -1 (!ok())
+    // Why passing raw fd? When fd is passed as reference, Clang analyzer sees that the variable
+    // `fd` is a moved-from object. To work-around the issue, unwrap the raw fd from the outer `fd`,
+    // pass the raw fd by value to the lambda, and then finally wrap it in unique_fd inside the
+    // lambda.
+    return setupClient([&, raw = fd.release()](const std::vector<uint8_t>& sessionId,
+                                               bool incoming) -> status_t {
+        unique_fd fd(raw);
         if (!fd.ok()) {
             fd = request();
             if (!fd.ok()) return BAD_VALUE;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 4ddbce7..2e7084e 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -125,8 +125,8 @@
         auto&& [it, inserted] = mNodeForAddress.insert({RpcWireAddress::toRaw(address),
                                                         BinderNode{
                                                                 .binder = binder,
-                                                                .timesSent = 1,
                                                                 .sentRef = binder,
+                                                                .timesSent = 1,
                                                         }});
         if (inserted) {
             *outAddress = it->first;
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index c0454b6..8deb2fe 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -17,10 +17,10 @@
 #pragma once
 
 #include <binder/IBinder.h>
-#include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
 #include <utils/threads.h>
 
+#include <map>
 #include <unordered_map>
 #include <variant>
 
@@ -110,7 +110,7 @@
             IBinder::object_cleanup_func func;
         };
 
-        KeyedVector<const void*, entry_t> mObjects;
+        std::map<const void*, entry_t> mObjects;
     };
 
     class PrivateAccessor {
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index ea40db8..bb55831 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -56,8 +56,16 @@
     static const int DUMP_FLAG_PROTO = 1 << 4;
 
     /**
-     * Retrieve an existing service, blocking for a few seconds
-     * if it doesn't yet exist.
+     * Retrieve an existing service, blocking for a few seconds if it doesn't yet exist. This
+     * does polling. A more efficient way to make sure you unblock as soon as the service is
+     * available is to use waitForService or to use service notifications.
+     *
+     * Warning: when using this API, typically, you should call it in a loop. It's dangerous to
+     * assume that nullptr could mean that the service is not available. The service could just
+     * be starting. Generally, whether a service exists, this information should be declared
+     * externally (for instance, an Android feature might imply the existence of a service,
+     * a system property, or in the case of services in the VINTF manifest, it can be checked
+     * with isDeclared).
      */
     virtual sp<IBinder>         getService( const String16& name) const = 0;
 
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 2c471c6..7ea9be7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -268,7 +268,11 @@
     const char* getMessage() const { return AStatus_getMessage(get()); }
 
     std::string getDescription() const {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 30, *)) {
+#else
+        if (__ANDROID_API__ >= 30) {
+#endif
             const char* cStr = AStatus_getDescription(get());
             std::string ret = cStr;
             AStatus_deleteDescription(cStr);
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 09411e7..b3bc7f4 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -256,7 +256,11 @@
     // ourselves. The defaults are harmless.
     AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
 #ifdef HAS_BINDER_SHELL_COMMAND
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
     if (__builtin_available(android 30, *)) {
+#else
+    if (__ANDROID_API__ >= 30) {
+#endif
         AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
     }
 #endif
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 972eca7..28819bb 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -51,7 +51,11 @@
     AParcelableHolder(const AParcelableHolder& other)
         : mParcel(AParcel_create()), mStability(other.mStability) {
         // AParcelableHolder has been introduced in 31.
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
                                AParcel_getDataSize(other.mParcel.get()));
         }
@@ -63,13 +67,21 @@
 
     binder_status_t writeToParcel(AParcel* parcel) const {
         RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             int32_t size = AParcel_getDataSize(this->mParcel.get());
             RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
         } else {
             return STATUS_INVALID_OPERATION;
         }
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             int32_t size = AParcel_getDataSize(this->mParcel.get());
             RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
         } else {
@@ -79,7 +91,11 @@
     }
 
     binder_status_t readFromParcel(const AParcel* parcel) {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_reset(mParcel.get());
         } else {
             return STATUS_INVALID_OPERATION;
@@ -99,7 +115,11 @@
             return STATUS_BAD_VALUE;
         }
 
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
         } else {
             status = STATUS_INVALID_OPERATION;
@@ -115,7 +135,11 @@
         if (this->mStability > T::_aidl_stability) {
             return STATUS_BAD_VALUE;
         }
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_reset(mParcel.get());
         } else {
             return STATUS_INVALID_OPERATION;
@@ -129,7 +153,11 @@
     binder_status_t getParcelable(std::optional<T>* ret) const {
         const std::string parcelableDesc(T::descriptor);
         AParcel_setDataPosition(mParcel.get(), 0);
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             if (AParcel_getDataSize(mParcel.get()) == 0) {
                 *ret = std::nullopt;
                 return STATUS_OK;
@@ -153,7 +181,11 @@
     }
 
     void reset() {
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
         if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
             AParcel_reset(mParcel.get());
         }
     }
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 2a66941..dfa8ea2 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -53,11 +53,19 @@
 /**
  * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
  * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
- * for calling AIBinder_decStrong).
+ * for calling AIBinder_decStrong). This does polling. A more efficient way to make sure you
+ * unblock as soon as the service is available is to use AIBinder_waitForService.
  *
  * WARNING: when using this API across an APEX boundary, do not use with unstable
  * AIDL services. TODO(b/139325195)
  *
+ * WARNING: when using this API, typically, you should call it in a loop. It's dangerous to
+ * assume that nullptr could mean that the service is not available. The service could just
+ * be starting. Generally, whether a service exists, this information should be declared
+ * externally (for instance, an Android feature might imply the existence of a service,
+ * a system property, or in the case of services in the VINTF manifest, it can be checked
+ * with AServiceManager_isDeclared).
+ *
  * \param instance identifier of the service used to lookup the service.
  */
 __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 1513eca..45a6d47 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -31,6 +31,7 @@
 #include <android-base/unique_fd.h>
 #include <bpf/BpfMap.h>
 #include <cputimeinstate.h>
+#include <cutils/android_filesystem_config.h>
 #include <libbpf.h>
 
 namespace android {
@@ -219,6 +220,7 @@
         uint32_t totalFreqsCount = totalTimes.size();
         std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
         for (auto const &[uid, uidTimes]: *allUid) {
+            if (uid == AID_SDK_SANDBOX) continue;
             for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
                 allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
             }
@@ -646,5 +648,55 @@
     }
 }
 
+void *forceSwitchWithUid(void *uidPtr) {
+    if (!uidPtr) return nullptr;
+    setuid(*(uint32_t *)uidPtr);
+
+    // Sleep briefly to trigger a context switch, ensuring we see at least one update.
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1000000;
+    nanosleep(&ts, NULL);
+    return nullptr;
+}
+
+TEST_F(TimeInStateTest, SdkSandboxUid) {
+    // Find an unused app UID and its corresponding SDK sandbox uid.
+    uint32_t appUid = AID_APP_START, sandboxUid;
+    {
+        auto times = getUidsCpuFreqTimes();
+        ASSERT_TRUE(times.has_value());
+        ASSERT_FALSE(times->empty());
+        for (const auto &kv : *times) {
+            if (kv.first > AID_APP_END) break;
+            appUid = std::max(appUid, kv.first);
+        }
+        appUid++;
+        sandboxUid = appUid + (AID_SDK_SANDBOX_PROCESS_START - AID_APP_START);
+    }
+
+    // Create a thread to run with the fake sandbox uid.
+    pthread_t thread;
+    ASSERT_EQ(pthread_create(&thread, NULL, &forceSwitchWithUid, &sandboxUid), 0);
+    pthread_join(thread, NULL);
+
+    // Confirm we recorded stats for appUid and AID_SDK_SANDBOX but not sandboxUid
+    auto allTimes = getUidsCpuFreqTimes();
+    ASSERT_TRUE(allTimes.has_value());
+    ASSERT_FALSE(allTimes->empty());
+    ASSERT_NE(allTimes->find(appUid), allTimes->end());
+    ASSERT_NE(allTimes->find(AID_SDK_SANDBOX), allTimes->end());
+    ASSERT_EQ(allTimes->find(sandboxUid), allTimes->end());
+
+    auto allConcurrentTimes = getUidsConcurrentTimes();
+    ASSERT_TRUE(allConcurrentTimes.has_value());
+    ASSERT_FALSE(allConcurrentTimes->empty());
+    ASSERT_NE(allConcurrentTimes->find(appUid), allConcurrentTimes->end());
+    ASSERT_NE(allConcurrentTimes->find(AID_SDK_SANDBOX), allConcurrentTimes->end());
+    ASSERT_EQ(allConcurrentTimes->find(sandboxUid), allConcurrentTimes->end());
+
+    ASSERT_TRUE(clearUidTimes(appUid));
+}
+
 } // namespace bpf
 } // namespace android
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 8e23eb87..ec4c7c1 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -182,7 +182,8 @@
              static_cast<uint32_t>(mPendingTransactions.size()));
     SurfaceComposerClient::Transaction t;
     mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
-    t.setApplyToken(mApplyToken).apply();
+    // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+    t.setApplyToken(mApplyToken).apply(false, true);
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
@@ -230,7 +231,8 @@
         }
     }
     if (applyTransaction) {
-        t.setApplyToken(mApplyToken).apply();
+        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+        t.setApplyToken(mApplyToken).apply(false, true);
     }
 }
 
@@ -551,7 +553,13 @@
 
     mergePendingTransactions(t, bufferItem.mFrameNumber);
     if (applyTransaction) {
-        t->setApplyToken(mApplyToken).apply();
+        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+        t->setApplyToken(mApplyToken).apply(false, true);
+        mAppliedLastTransaction = true;
+        mLastAppliedFrameNumber = bufferItem.mFrameNumber;
+    } else {
+        t->setBufferHasBarrier(mSurfaceControl, mLastAppliedFrameNumber);
+        mAppliedLastTransaction = false;
     }
 
     BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
@@ -857,7 +865,8 @@
 
     SurfaceComposerClient::Transaction t;
     mergePendingTransactions(&t, frameNumber);
-    t.setApplyToken(mApplyToken).apply();
+    // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+    t.setApplyToken(mApplyToken).apply(false, true);
 }
 
 void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
@@ -1050,7 +1059,8 @@
                  static_cast<uint32_t>(mPendingTransactions.size()));
         SurfaceComposerClient::Transaction t;
         mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
-        t.setApplyToken(mApplyToken).apply();
+        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
+        t.setApplyToken(mApplyToken).apply(false, true);
     }
 
     // Clear sync states
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 5ab0abc..5532c6e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -111,7 +111,13 @@
 
         SAFE_PARCEL(data.writeUint64, transactionId);
 
-        return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
+        if (flags & ISurfaceComposer::eOneWay) {
+            return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
+                    data, &reply, IBinder::FLAG_ONEWAY);
+        } else {
+            return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
+                    data, &reply);
+        }
     }
 
     void bootFinished() override {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6944d38..338ff11 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -796,6 +796,8 @@
 
     SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote());
     SAFE_PARCEL(output->writeUint64, cachedBuffer.id);
+    SAFE_PARCEL(output->writeBool, hasBarrier);
+    SAFE_PARCEL(output->writeUint64, barrierFrameNumber);
 
     return NO_ERROR;
 }
@@ -832,6 +834,9 @@
     cachedBuffer.token = tmpBinder;
     SAFE_PARCEL(input->readUint64, &cachedBuffer.id);
 
+    SAFE_PARCEL(input->readBool, &hasBarrier);
+    SAFE_PARCEL(input->readUint64, &barrierFrameNumber);
+
     return NO_ERROR;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 26ccda5..27856ce 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -930,7 +930,7 @@
     }
 }
 
-status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
+status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
     if (mStatus != NO_ERROR) {
         return mStatus;
     }
@@ -984,6 +984,14 @@
     if (mAnimation) {
         flags |= ISurfaceComposer::eAnimation;
     }
+    if (oneWay) {
+      if (mForceSynchronous) {
+          ALOGE("Transaction attempted to set synchronous and one way at the same time"
+                " this is an invalid request. Synchronous will win for safety");
+      } else {
+          flags |= ISurfaceComposer::eOneWay;
+      }
+    }
 
     // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
     // it is equivalent for none
@@ -1399,6 +1407,18 @@
     return bufferData;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferHasBarrier(
+        const sp<SurfaceControl>& sc, uint64_t barrierFrameNumber) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->bufferData->hasBarrier = true;
+    s->bufferData->barrierFrameNumber = barrierFrameNumber;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
         const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
         const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 265ae24..65fc04d 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -254,6 +254,21 @@
     // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with
     // additional scales in the hierarchy.
     bool mUpdateDestinationFrame GUARDED_BY(mMutex) = true;
+
+    // We send all transactions on our apply token over one-way binder calls to avoid blocking
+    // client threads. All of our transactions remain in order, since they are one-way binder calls
+    // from a single process, to a single interface. However once we give up a Transaction for sync
+    // we can start to have ordering issues. When we return from sync to normal frame production,
+    // we wait on the commit callback of sync frames ensuring ordering, however we don't want to
+    // wait on the commit callback for every normal frame (since even emitting them has a
+    // performance cost) this means we need a method to ensure frames are in order when switching
+    // from one-way application on our apply token, to application on some other apply token. We
+    // make use of setBufferHasBarrier to declare this ordering. This boolean simply tracks when we
+    // need to set this flag, notably only in the case where we are transitioning from a previous
+    // transaction applied by us (one way, may not yet have reached server) and an upcoming
+    // transaction that will be applied by some sync consumer.
+    bool mAppliedLastTransaction = false;
+    uint64_t mLastAppliedFrameNumber = 0;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 4dfc383..0a3cc19 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -113,6 +113,7 @@
         // android.permission.ACCESS_SURFACE_FLINGER
         eEarlyWakeupStart = 0x08,
         eEarlyWakeupEnd = 0x10,
+        eOneWay = 0x20
     };
 
     enum VsyncSource {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 885b4ae..0f37dab 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -89,6 +89,8 @@
     // Used by BlastBufferQueue to forward the framenumber generated by the
     // graphics producer.
     uint64_t frameNumber = 0;
+    bool hasBarrier = false;
+    uint64_t barrierFrameNumber = 0;
 
     // Listens to when the buffer is safe to be released. This is used for blast
     // layers only. The callback includes a release fence as well as the graphic
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6c79b5b..c8ac166 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -461,7 +461,7 @@
         // Clears the contents of the transaction without applying it.
         void clear();
 
-        status_t apply(bool synchronous = false);
+        status_t apply(bool synchronous = false, bool oneWay = false);
         // Merge another transaction in to this one, clearing other
         // as if it had been applied.
         Transaction& merge(Transaction&& other);
@@ -521,6 +521,27 @@
                                const std::optional<uint64_t>& frameNumber = std::nullopt,
                                ReleaseBufferCallback callback = nullptr);
         std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
+
+        /**
+         * If this transaction, has a a buffer set for the given SurfaceControl
+         * mark that buffer as ordered after a given barrierFrameNumber.
+         *
+         * SurfaceFlinger will refuse to apply this transaction until after
+         * the frame in barrierFrameNumber has been applied. This transaction may
+         * be applied in the same frame as the barrier buffer or after.
+         *
+         * This is only designed to be used to handle switches between multiple
+         * apply tokens, as explained in the comment for BLASTBufferQueue::mAppliedLastTransaction.
+         *
+         * Has to be called after setBuffer.
+         *
+         * WARNING:
+         * This API is very dangerous to the caller, as if you invoke it without
+         * a frameNumber you have not yet submitted, you can dead-lock your
+         * SurfaceControl's transaction queue.
+         */
+        Transaction& setBufferHasBarrier(const sp<SurfaceControl>& sc,
+                                         uint64_t barrierFrameNumber);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
         Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
         Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 18a6bae..0aca24a 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -311,6 +311,7 @@
             ? 0
             : mBufferInfo.mBufferSlot;
     compositionState->acquireFence = mBufferInfo.mFence;
+    compositionState->frameNumber = mBufferInfo.mFrameNumber;
     compositionState->sidebandStreamHasFrame = false;
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 669eaad..8a696f1 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -141,6 +141,8 @@
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
+    uint64_t mPreviousBarrierFrameNumber = 0;
+
     bool mReleasePreviousBuffer = false;
 
     // Stores the last set acquire fence signal time used to populate the callback handle's acquire
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8bf7f8f..283fe86 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -166,6 +166,7 @@
     int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
     sp<Fence> acquireFence = Fence::NO_FENCE;
     Region surfaceDamage;
+    uint64_t frameNumber = 0;
 
     // The handle to use for a sideband stream for this layer
     sp<NativeHandle> sidebandStream;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 14324de..cb00e71 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -28,6 +28,7 @@
 #include "DisplayHardware/Hal.h"
 #include "math/HashCombine.h"
 
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
 namespace std {
@@ -395,6 +396,21 @@
                                 return std::vector<std::string>{base::StringPrintf("%p", p)};
                             }};
 
+    static auto constexpr BufferEquals = [](const wp<GraphicBuffer>& lhs,
+                                            const wp<GraphicBuffer>& rhs) -> bool {
+        // Avoid a promotion if the wp<>'s aren't equal
+        if (lhs != rhs) return false;
+
+        // Even if the buffer didn't change, check to see if we need to act as if the buffer changed
+        // anyway. Specifically, look to see if the buffer is FRONT_BUFFER & if so act as if it's
+        // always different
+        using ::aidl::android::hardware::graphics::common::BufferUsage;
+        sp<GraphicBuffer> promotedBuffer = lhs.promote();
+        return !(promotedBuffer &&
+                 ((promotedBuffer->getUsage() & static_cast<int64_t>(BufferUsage::FRONT_BUFFER)) !=
+                  0));
+    };
+
     OutputLayerState<wp<GraphicBuffer>, LayerStateField::Buffer>
             mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
                     [](const wp<GraphicBuffer>& buffer) {
@@ -403,7 +419,14 @@
                                 base::StringPrintf("%p",
                                                    promotedBuffer ? promotedBuffer.get()
                                                                   : nullptr)};
-                    }};
+                    },
+                    BufferEquals};
+
+    // Even if the same buffer is passed to BLAST's setBuffer(), we still increment the frame
+    // number and need to treat it as if the buffer changed. Otherwise we break existing
+    // front-buffer rendering paths (such as egl's EGL_SINGLE_BUFFER).
+    OutputLayerState<uint64_t, LayerStateField::Buffer> mFrameNumber{
+            [](auto layer) { return layer->getLayerFE().getCompositionState()->frameNumber; }};
 
     int64_t mFramesSinceBufferUpdate = 0;
 
@@ -453,7 +476,7 @@
                                       return hash;
                                   }};
 
-    static const constexpr size_t kNumNonUniqueFields = 16;
+    static const constexpr size_t kNumNonUniqueFields = 17;
 
     std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
         std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -472,6 +495,7 @@
                 &mAlpha,        &mLayerMetadata,  &mVisibleRegion,        &mOutputDataspace,
                 &mPixelFormat,  &mColorTransform, &mCompositionType,      &mSidebandStream,
                 &mBuffer,       &mSolidColor,     &mBackgroundBlurRadius, &mBlurRegions,
+                &mFrameNumber,
         };
     }
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e4bd325..7e650a1 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1203,8 +1203,12 @@
     // because high frequency consumes extra battery.
     const bool expensiveBlurs =
             refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
-    const bool expensiveRenderingExpected =
-            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 || expensiveBlurs;
+    const bool expensiveRenderingExpected = expensiveBlurs ||
+            std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+                        [outputDataspace =
+                                 clientCompositionDisplay.outputDataspace](const auto& layer) {
+                            return layer.sourceDataspace != outputDataspace;
+                        });
     if (expensiveRenderingExpected) {
         setExpensiveRenderingExpected(true);
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 3e983f3..723593d 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -325,7 +325,8 @@
     // For hdr content, treat the white point as the display brightness - HDR content should not be
     // boosted or dimmed.
     if (isHdrDataspace(state.dataspace) ||
-        getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits) {
+        getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
+        getOutput().getState().displayBrightnessNits == 0.f) {
         state.dimmingRatio = 1.f;
         state.whitePointNits = getOutput().getState().displayBrightnessNits;
     } else {
@@ -506,9 +507,18 @@
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    // Don't dim cached layers
-    const auto dimmingRatio =
-            outputDependentState.overrideInfo.buffer ? 1.f : outputDependentState.dimmingRatio;
+    // Cached layers are not dimmed, which means that composer should attempt to dim.
+    // Note that if the dimming ratio is large, then this may cause the cached layer
+    // to kick back into GPU composition :(
+    // Also note that this assumes that there are no HDR layers that are able to be cached.
+    // Otherwise, this could cause HDR layers to be dimmed twice.
+    const auto dimmingRatio = outputDependentState.overrideInfo.buffer
+            ? (getOutput().getState().displayBrightnessNits != 0.f
+                       ? std::clamp(getOutput().getState().sdrWhitePointNits /
+                                            getOutput().getState().displayBrightnessNits,
+                                    0.f, 1.f)
+                       : 1.f)
+            : outputDependentState.dimmingRatio;
 
     if (auto error = hwcLayer->setBrightness(dimmingRatio); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(),
@@ -788,6 +798,7 @@
             }};
     settings.sourceDataspace = getState().overrideInfo.dataspace;
     settings.alpha = 1.0f;
+    settings.whitePointNits = getOutput().getState().sdrWhitePointNits;
 
     return {static_cast<LayerFE::LayerSettings>(settings)};
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 2203f22..42c1263 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -20,6 +20,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <math/HashCombine.h>
@@ -216,9 +217,8 @@
     renderengine::LayerSettings holePunchSettings;
     renderengine::LayerSettings holePunchBackgroundSettings;
     if (mHolePunchLayer) {
-        auto clientCompositionList =
-                mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
-                        targetSettings);
+        auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE();
+        auto clientCompositionList = layerFE.prepareClientCompositionList(targetSettings);
         // Assume that the final layer contains the buffer that we want to
         // replace with a hole punch.
         holePunchSettings = clientCompositionList.back();
@@ -227,7 +227,8 @@
         holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
         holePunchSettings.disableBlending = true;
         holePunchSettings.alpha = 0.0f;
-        holePunchSettings.name = std::string("hole punch layer");
+        holePunchSettings.name =
+                android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName());
         layerSettings.push_back(holePunchSettings);
 
         // Add a solid background as the first layer in case there is no opaque
@@ -390,7 +391,10 @@
         const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr;
         base::StringAppendF(&result, "    Override buffer: %p\n", b);
     }
-    base::StringAppendF(&result, "    HolePunchLayer: %p\n", mHolePunchLayer);
+    base::StringAppendF(&result, "    HolePunchLayer: %p\t%s\n", mHolePunchLayer,
+                        mHolePunchLayer
+                                ? mHolePunchLayer->getOutputLayer()->getLayerFE().getDebugName()
+                                : "");
 
     if (mLayers.size() == 1) {
         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dda0822..8eb1946 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -657,7 +657,7 @@
     EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
 }
 
-TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) {
+TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsAndDimmingRatioCorrectly) {
     mOutputState.sdrWhitePointNits = 200.f;
     mOutputState.displayBrightnessNits = 800.f;
 
@@ -665,12 +665,15 @@
     mLayerFEState.isColorspaceAgnostic = false;
     mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
     EXPECT_EQ(mOutputState.sdrWhitePointNits, mOutputLayer.getState().whitePointNits);
+    EXPECT_EQ(mOutputState.sdrWhitePointNits / mOutputState.displayBrightnessNits,
+              mOutputLayer.getState().dimmingRatio);
 
     mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
     mLayerFEState.isColorspaceAgnostic = false;
     mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+    EXPECT_EQ(1.f, mOutputLayer.getState().dimmingRatio);
 }
 
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
@@ -750,9 +753,10 @@
     static constexpr bool kLayerGenericMetadata1Mandatory = true;
     static constexpr bool kLayerGenericMetadata2Mandatory = true;
     static constexpr float kWhitePointNits = 200.f;
+    static constexpr float kSdrWhitePointNits = 100.f;
     static constexpr float kDisplayBrightnessNits = 400.f;
     static constexpr float kLayerBrightness = kWhitePointNits / kDisplayBrightnessNits;
-    static constexpr float kFullLayerBrightness = 1.f;
+    static constexpr float kOverrideLayerBrightness = kSdrWhitePointNits / kDisplayBrightnessNits;
 
     static const half4 kColor;
     static const Rect kDisplayFrame;
@@ -798,6 +802,7 @@
         mLayerFEState.acquireFence = kFence;
 
         mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
+        mOutputState.sdrWhitePointNits = kSdrWhitePointNits;
 
         EXPECT_CALL(mOutput, getDisplayColorProfile())
                 .WillRepeatedly(Return(&mDisplayColorProfile));
@@ -1117,7 +1122,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kSkipAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1133,7 +1138,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kSkipAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1149,7 +1154,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1165,7 +1170,7 @@
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage, kFullLayerBrightness);
+                              kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 0b1c262..bd4ff13 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "LayerStateTest"
 
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/planner/LayerState.h>
 #include <compositionengine/mock/LayerFE.h>
@@ -29,7 +30,8 @@
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
-using aidl::android::hardware::graphics::composer3::Composition;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::composer3::Composition;
 
 namespace android::compositionengine::impl::planner {
 namespace {
@@ -359,6 +361,54 @@
     EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
 }
 
+TEST_F(LayerStateTest, updateBufferSingleBufferedLegacy) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+
+    for (uint64_t i = 0; i < 10; i++) {
+        layerFECompositionStateTwo.frameNumber = i;
+        setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                           layerFECompositionStateTwo);
+        Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+    }
+}
+
+TEST_F(LayerStateTest, updateBufferSingleBufferedUsage) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer->usage = static_cast<uint64_t>(BufferUsage::FRONT_BUFFER);
+    setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+
+    for (uint64_t i = 0; i < 10; i++) {
+        setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                           layerFECompositionStateTwo);
+        Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+    }
+}
+
 TEST_F(LayerStateTest, compareBuffer) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 712cd5b..f2af85e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -52,7 +52,7 @@
         return;
     }
 
-    compositor.composite(frameTime);
+    compositor.composite(frameTime, mVsyncId);
     compositor.sample();
 }
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 9532e26..4082e26 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -35,7 +35,7 @@
 
 struct ICompositor {
     virtual bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) = 0;
-    virtual void composite(nsecs_t frameTime) = 0;
+    virtual void composite(nsecs_t frameTime, int64_t vsyncId) = 0;
     virtual void sample() = 0;
 
 protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 350d709..d1ac1d3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2199,8 +2199,8 @@
     return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
 }
 
-void SurfaceFlinger::composite(nsecs_t frameTime) {
-    ATRACE_CALL();
+void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) {
+    ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
     MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
     if (mPowerHintSessionData.sessionEnabled) {
         mPowerHintSessionData.compositeStart = systemTime();
@@ -3674,11 +3674,12 @@
     return false;
 }
 
-void SurfaceFlinger::flushPendingTransactionQueues(
+int SurfaceFlinger::flushPendingTransactionQueues(
         std::vector<TransactionState>& transactions,
-        std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
+        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
         std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
         bool tryApplyUnsignaled) {
+    int transactionsPendingBarrier = 0;
     auto it = mPendingTransactionQueues.begin();
     while (it != mPendingTransactionQueues.end()) {
         auto& [applyToken, transactionQueue] = *it;
@@ -3701,8 +3702,21 @@
                 setTransactionFlags(eTransactionFlushNeeded);
                 break;
             }
+            if (ready == TransactionReadiness::NotReadyBarrier) {
+                transactionsPendingBarrier++;
+                setTransactionFlags(eTransactionFlushNeeded);
+                break;
+            }
             transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                bufferLayersReadyToPresent.insert(state.surface);
+                const bool frameNumberChanged = 
+                    state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+                if (frameNumberChanged) {
+                    bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
+                } else {
+                    // Barrier function only used for BBQ which always includes a frame number
+                    bufferLayersReadyToPresent[state.surface] =
+                        std::numeric_limits<uint64_t>::max();
+                }
             });
             const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
             if (appliedUnsignaled) {
@@ -3720,6 +3734,7 @@
             it = std::next(it, 1);
         }
     }
+    return transactionsPendingBarrier;
 }
 
 bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
@@ -3728,19 +3743,21 @@
     // states) around outside the scope of the lock
     std::vector<TransactionState> transactions;
     // Layer handles that have transactions with buffers that are ready to be applied.
-    std::unordered_set<sp<IBinder>, SpHash<IBinder>> bufferLayersReadyToPresent;
+    std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
     std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
     {
         Mutex::Autolock _l(mStateLock);
         {
             Mutex::Autolock _l(mQueueLock);
 
+            int lastTransactionsPendingBarrier = 0;
+            int transactionsPendingBarrier = 0;
             // First collect transactions from the pending transaction queues.
             // We are not allowing unsignaled buffers here as we want to
             // collect all the transactions from applyTokens that are ready first.
-            flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                                          applyTokensWithUnsignaledTransactions,
-                                          /*tryApplyUnsignaled*/ false);
+            transactionsPendingBarrier =
+                    flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+                            applyTokensWithUnsignaledTransactions, /*tryApplyUnsignaled*/ false);
 
             // Second, collect transactions from the transaction queue.
             // Here as well we are not allowing unsignaled buffers for the same
@@ -3765,18 +3782,48 @@
                                                          /*tryApplyUnsignaled*/ false);
                 }();
                 ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
-                if (ready == TransactionReadiness::NotReady) {
+                if (ready != TransactionReadiness::Ready) {
+                    if (ready == TransactionReadiness::NotReadyBarrier) {
+                        transactionsPendingBarrier++;
+                    }
                     mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
                     transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                        bufferLayersReadyToPresent.insert(state.surface);
-                    });
+                        const bool frameNumberChanged = 
+                            state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+                        if (frameNumberChanged) {
+                            bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
+                        } else {
+                            // Barrier function only used for BBQ which always includes a frame number.
+                            // This value only used for barrier logic.
+                            bufferLayersReadyToPresent[state.surface] =
+                                std::numeric_limits<uint64_t>::max();
+                        }
+                      });
                     transactions.emplace_back(std::move(transaction));
                 }
                 mTransactionQueue.pop_front();
                 ATRACE_INT("TransactionQueue", mTransactionQueue.size());
             }
 
+            // Transactions with a buffer pending on a barrier may be on a different applyToken
+            // than the transaction which satisfies our barrier. In fact this is the exact use case
+            // that the primitive is designed for. This means we may first process
+            // the barrier dependent transaction, determine it ineligible to complete
+            // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+            // The barrier dependent transaction was eligible to be presented in this frame
+            // but we would have prevented it without case. To fix this we continually
+            // loop through flushPendingTransactionQueues until we perform an iteration
+            // where the number of transactionsPendingBarrier doesn't change. This way
+            // we can continue to resolve dependency chains of barriers as far as possible.
+            while (lastTransactionsPendingBarrier != transactionsPendingBarrier) {
+                lastTransactionsPendingBarrier = transactionsPendingBarrier;
+                transactionsPendingBarrier =
+                    flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
+                        applyTokensWithUnsignaledTransactions,
+                        /*tryApplyUnsignaled*/ false);
+            }
+
             // We collected all transactions that could apply without latching unsignaled buffers.
             // If we are allowing latch unsignaled of some form, now it's the time to go over the
             // transactions that were not applied and try to apply them unsignaled.
@@ -3892,7 +3939,8 @@
 auto SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
-        const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
+        const std::unordered_map<
+            sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
         size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
     ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
@@ -3930,6 +3978,17 @@
             continue;
         }
 
+        if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
+            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
+            const bool willApplyBarrierFrame =
+                (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
+                (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
+            if (!willApplyBarrierFrame) {
+                ATRACE_NAME("NotReadyBarrier");
+                return TransactionReadiness::NotReadyBarrier;
+            }
+        }
+
         const bool allowLatchUnsignaled = tryApplyUnsignaled &&
                 shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
         ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
@@ -3950,8 +4009,8 @@
         if (s.hasBufferChanges()) {
             // If backpressure is enabled and we already have a buffer to commit, keep the
             // transaction in the queue.
-            const bool hasPendingBuffer =
-                    bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
+            const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
+                bufferLayersReadyToPresent.end();
             if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
                 ATRACE_NAME("hasPendingBuffer");
                 return TransactionReadiness::NotReady;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5829838..86c8333 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -671,7 +671,7 @@
 
     // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
     // via RenderEngine and the Composer HAL, respectively.
-    void composite(nsecs_t frameTime) override;
+    void composite(nsecs_t frameTime, int64_t vsyncId) override;
 
     // Samples the composited frame via RegionSamplingThread.
     void sample() override;
@@ -760,9 +760,9 @@
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
 
-    void flushPendingTransactionQueues(
+    int flushPendingTransactionQueues(
             std::vector<TransactionState>& transactions,
-            std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
+            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
             std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
             bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock);
 
@@ -789,13 +789,15 @@
     void commitOffscreenLayers();
     enum class TransactionReadiness {
         NotReady,
+        NotReadyBarrier,
         Ready,
         ReadyUnsignaled,
     };
     TransactionReadiness transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
-            const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
+            const std::unordered_map<
+                sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
             size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock);
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
     bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index c1112d2..d249b60 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -431,6 +431,7 @@
         layer.bufferData->frameNumber = bufferProto.frame_number();
         layer.bufferData->flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
         layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
+        layer.bufferData->acquireFence = Fence::NO_FENCE;
     }
 
     if (proto.what() & layer_state_t::eApiChanged) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 2fcf856..b796dfe 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -271,7 +271,7 @@
 private:
     // ICompositor overrides:
     bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t) override {}
+    void composite(nsecs_t, int64_t) override {}
     void sample() override {}
 };
 }; // namespace scheduler
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 1dd7dea..e0aa0b1 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -33,7 +33,7 @@
 
 struct NoOpCompositor final : ICompositor {
     bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t) override {}
+    void composite(nsecs_t, int64_t) override {}
     void sample() override {}
 } gNoOpCompositor;
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 364d8f1..4708572 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -110,7 +110,7 @@
 private:
     // ICompositor overrides:
     bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t) override {}
+    void composite(nsecs_t, int64_t) override {}
     void sample() override {}
 };
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index d83b9bb..fe0564e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -334,16 +334,14 @@
     /* ------------------------------------------------------------------------
      * Forwarding for functions being tested
      */
-
     nsecs_t commit() {
-        constexpr int64_t kVsyncId = 123;
         const nsecs_t now = systemTime();
         const nsecs_t expectedVsyncTime = now + 10'000'000;
         mFlinger->commit(now, kVsyncId, expectedVsyncTime);
         return now;
     }
 
-    void commitAndComposite() { mFlinger->composite(commit()); }
+    void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
 
     auto createDisplay(const String8& displayName, bool secure) {
         return mFlinger->createDisplay(displayName, secure);
@@ -877,6 +875,8 @@
     };
 
 private:
+    constexpr static int64_t kVsyncId = 123;
+
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger;
     scheduler::mock::SchedulerCallback mSchedulerCallback;