Merge changes from topics "presubmit-am-5b86c75ec52142ad9dfd0837f3fad249", "presubmit-am-c314b40f446d47e6aa51654f7a3e4f49" into tm-dev

* changes:
  SF: Unify data types for display modes
  SF: Clean up plumbing for boot display mode
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 08a3d9a..6fb9a4d 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -1225,10 +1225,7 @@
 
         if (ret < 0) {
             for (int i = optind; i < argc; i++) {
-                if (!setCategoryEnable(argv[i])) {
-                    fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]);
-                    exit(1);
-                }
+                setCategoryEnable(argv[i]);
             }
             break;
         }
@@ -1344,10 +1341,10 @@
         // contain entries from only one CPU can cause "begin" entries without a
         // matching "end" entry to show up if a task gets migrated from one CPU to
         // another.
-        if (!onlyUserspace)
+        if (!onlyUserspace) {
             ok = clearTrace();
-
-        writeClockSyncMarker();
+            writeClockSyncMarker();
+        }
         if (ok && !async && !traceStream) {
             // Sleep to allow the trace to be captured.
             struct timespec timeLeft;
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 7eb5cb4..7bad351 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -726,8 +726,7 @@
 
     if (flags & FLAG_STORAGE_SDK) {
         // Safe to ignore status since we can retry creating this by calling reconcileSdkData
-        auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId,
-                                                           previousAppId, seInfo, flags);
+        auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags);
         if (!ignore.isOk()) {
             PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName;
         }
@@ -746,7 +745,7 @@
  */
 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 appId, int32_t flags) {
     int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
     if (sdkSandboxUid == -1) {
         // There no valid sdk sandbox process for this app. Skip creation of data directory
@@ -765,7 +764,7 @@
         // /data/misc_{ce,de}/<user-id>/sdksandbox directory gets created by vold
         // during user creation
 
-        // Prepare the app directory
+        // Prepare the package directory
         auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId,
                                                                      packageName.c_str());
 #if SDK_DEBUG
@@ -775,27 +774,6 @@
         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
-        auto sharedPath = create_data_misc_sdk_sandbox_shared_path(uuid_, isCeData, userId,
-                                                                   packageName.c_str());
-
-        int32_t previousSdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId);
-        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 sdksandbox data"));
-        }
-        auto status = createAppDataDirs(sharedPath, sdkSandboxUid, AID_NOBODY,
-                                        &previousSdkSandboxUid, cacheGid, seInfo, 0700 | S_ISGID);
-        if (!status.isOk()) {
-            return status;
-        }
-
-        // TODO(b/211763739): We also need to handle art profile creations
-
-        // TODO(b/211763739): And return the CE inode of the sdksandbox root directory and
-        // app directory under it so we can clear contents while CE storage is locked
     }
 
     return ok();
@@ -848,8 +826,8 @@
         const android::os::ReconcileSdkDataArgs& args) {
     // 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);
+    return reconcileSdkData(args.uuid, args.packageName, args.subDirNames, args.userId, args.appId,
+                            args.previousAppId, args.seInfo, args.flags);
 }
 
 /**
@@ -863,17 +841,14 @@
  *   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) {
+binder::Status InstalldNativeService::reconcileSdkData(const std::optional<std::string>& uuid,
+                                                       const std::string& packageName,
+                                                       const std::vector<std::string>& subDirNames,
+                                                       int userId, int appId, int previousAppId,
+                                                       const std::string& seInfo, int flags) {
     ENFORCE_UID(AID_SYSTEM);
     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
@@ -882,16 +857,9 @@
 
     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);
+    const auto status =
+            createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags);
     if (!status.isOk()) {
         return status;
     }
@@ -905,37 +873,22 @@
         }
         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 existing sub-directories not referred in subDirNames
+        const std::unordered_set<std::string> expectedSubDirNames(subDirNames.begin(),
+                                                                  subDirNames.end());
+        const auto subDirHandler = [&packagePath, &expectedSubDirNames,
+                                    &res](const std::string& subDirName) {
             // 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);
+            // expectedSubDirNames
+            if (expectedSubDirNames.find(subDirName) == expectedSubDirNames.end()) {
+                auto path = packagePath + "/" + subDirName;
+                if (delete_dir_contents_and_dir(path) != 0) {
+                    res = error("Failed to delete " + path);
                     return;
                 }
-            } else {
-                // Otherwise, store it as existing sdk level directory
-                sdkNamesThatExist[sdkName] = filepath;
             }
         };
         const int ec = foreach_subdir(packagePath, subDirHandler);
@@ -944,19 +897,11 @@
             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());
-            }
+        // Now create the subDirNames
+        for (const auto& subDirName : subDirNames) {
+            const std::string path =
+                    create_data_misc_sdk_sandbox_sdk_path(uuid_, isCeData, userId,
+                                                          packageName.c_str(), subDirName.c_str());
 
             // Create the directory along with cache and code_cache
             const int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
@@ -3631,5 +3576,23 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::getOdexVisibility(
+        const std::string& packageName, const std::string& apkPath,
+        const std::string& instructionSet, const std::optional<std::string>& outputPath,
+        int32_t* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    CHECK_ARGUMENT_PATH(apkPath);
+    CHECK_ARGUMENT_PATH(outputPath);
+    LOCK_PACKAGE();
+
+    const char* apk_path = apkPath.c_str();
+    const char* instruction_set = instructionSet.c_str();
+    const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
+
+    *_aidl_return = get_odex_visibility(apk_path, instruction_set, oat_dir);
+    return *_aidl_return == -1 ? error() : ok();
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 1f0fc9c..e6be5d8 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -183,6 +183,11 @@
     binder::Status cleanupInvalidPackageDirs(const std::optional<std::string>& uuid, int32_t userId,
                                              int32_t flags);
 
+    binder::Status getOdexVisibility(const std::string& packageName, const std::string& apkPath,
+                                     const std::string& instructionSet,
+                                     const std::optional<std::string>& outputPath,
+                                     int32_t* _aidl_return);
+
 private:
     std::recursive_mutex mLock;
     std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock;
@@ -211,8 +216,7 @@
     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);
+                                                        int32_t flags);
     binder::Status clearSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid,
                                                        const std::string& packageName,
                                                        int32_t userId, int32_t flags);
@@ -221,8 +225,7 @@
                                                          int32_t userId, 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,
+                                    const std::vector<std::string>& subDirNames, int32_t userId,
                                     int32_t appId, int32_t previousAppId, const std::string& seInfo,
                                     int flags);
     binder::Status restoreconSdkDataLocked(const std::optional<std::string>& uuid,
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index 3f0fb6d..8ccab4c 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -30,6 +30,9 @@
     },
     {
       "name": "CtsCompilationTestCases"
+    },
+    {
+      "name": "SdkSandboxStorageHostTest"
     }
   ]
 }
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index e08e9b6..2b5a35e 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -130,6 +130,9 @@
 
     void cleanupInvalidPackageDirs(@nullable @utf8InCpp String uuid, int userId, int flags);
 
+    int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath,
+            @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath);
+
     const int FLAG_STORAGE_DE = 0x1;
     const int FLAG_STORAGE_CE = 0x2;
     const int FLAG_STORAGE_EXTERNAL = 0x4;
diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
index 2f794b1..583a36d 100644
--- a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
+++ b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl
@@ -20,8 +20,7 @@
 parcelable ReconcileSdkDataArgs {
     @nullable @utf8InCpp String uuid;
     @utf8InCpp String packageName;
-    @utf8InCpp List<String> sdkPackageNames;
-    @utf8InCpp List<String> randomSuffixes;
+    @utf8InCpp List<String> subDirNames;
     int userId;
     int appId;
     int previousAppId;
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 9647865..894c7d3 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -2773,13 +2773,23 @@
                          const std::string& profile_name,
                          const std::string& code_path,
                          const std::optional<std::string>& dex_metadata) {
-    // Prepare the current profile.
-    std::string cur_profile  = create_current_profile_path(user_id, package_name, profile_name,
-            /*is_secondary_dex*/ false);
-    uid_t uid = multiuser_get_uid(user_id, app_id);
-    if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) {
-        PLOG(ERROR) << "Failed to prepare " << cur_profile;
-        return false;
+    if (user_id != USER_NULL) {
+        if (user_id < 0) {
+            LOG(ERROR) << "Unexpected user ID " << user_id;
+            return false;
+        }
+
+        // Prepare the current profile.
+        std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name,
+                                                              /*is_secondary_dex*/ false);
+        uid_t uid = multiuser_get_uid(user_id, app_id);
+        if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) {
+            PLOG(ERROR) << "Failed to prepare " << cur_profile;
+            return false;
+        }
+    } else {
+        // Prepare the reference profile as the system user.
+        user_id = USER_SYSTEM;
     }
 
     // Check if we need to install the profile from the dex metadata.
@@ -2788,8 +2798,9 @@
     }
 
     // We have a dex metdata. Merge the profile into the reference profile.
-    unique_fd ref_profile_fd = open_reference_profile(uid, package_name, profile_name,
-            /*read_write*/ true, /*is_secondary_dex*/ false);
+    unique_fd ref_profile_fd =
+            open_reference_profile(multiuser_get_uid(user_id, app_id), package_name, profile_name,
+                                   /*read_write*/ true, /*is_secondary_dex*/ false);
     unique_fd dex_metadata_fd(TEMP_FAILURE_RETRY(
             open(dex_metadata->c_str(), O_RDONLY | O_NOFOLLOW)));
     unique_fd apk_fd(TEMP_FAILURE_RETRY(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW)));
@@ -2823,5 +2834,22 @@
     return true;
 }
 
+int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+    char oat_path[PKG_PATH_MAX];
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, /*is_secondary_dex=*/false,
+                             oat_path)) {
+        return -1;
+    }
+    struct stat st;
+    if (stat(oat_path, &st) == -1) {
+        if (errno == ENOENT) {
+            return ODEX_NOT_FOUND;
+        }
+        PLOG(ERROR) << "Could not stat " << oat_path;
+        return -1;
+    }
+    return (st.st_mode & S_IROTH) ? ODEX_IS_PUBLIC : ODEX_IS_PRIVATE;
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 12579b0..f7af929 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -98,10 +98,9 @@
                          const std::string& pkgname,
                          const std::string& profile_name);
 
-// Prepare the app profile for the given code path:
-//  - create the current profile using profile_name
-//  - merge the profile from the dex metadata file (if present) into
-//    the reference profile.
+// Prepares the app profile for the package at the given path:
+// - Creates the current profile for the given user ID, unless the user ID is `USER_NULL`.
+// - Merges the profile from the dex metadata file (if present) into the reference profile.
 bool prepare_app_profile(const std::string& package_name,
                          userid_t user_id,
                          appid_t app_id,
@@ -153,6 +152,12 @@
         bool is_release,
         bool is_debuggable_build);
 
+// Returns `ODEX_NOT_FOUND` if the optimized artifacts are not found, or `ODEX_IS_PUBLIC` if the
+// optimized artifacts are accessible by all apps, or `ODEX_IS_PRIVATE` if the optimized artifacts
+// are only accessible by this app, or -1 if failed to get the visibility of the optimized
+// artifacts.
+int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 00d8441..3623f9b 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -18,6 +18,8 @@
 #ifndef INSTALLD_CONSTANTS_H_
 #define INSTALLD_CONSTANTS_H_
 
+#include "cutils/multiuser.h"
+
 namespace android {
 namespace installd {
 
@@ -83,6 +85,15 @@
 constexpr int PROFILES_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA    = 2;
 constexpr int PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3;
 
+// NOTE: keep in sync with Installer.java
+constexpr int ODEX_NOT_FOUND = 0;
+constexpr int ODEX_IS_PUBLIC = 1;
+constexpr int ODEX_IS_PRIVATE = 2;
+
+// NOTE: keep in sync with UserHandle.java
+constexpr userid_t USER_NULL = -10000;
+constexpr userid_t USER_SYSTEM = 0;
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
 }  // namespace installd
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index f21a304..4eb30e2 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -650,6 +650,36 @@
 
         ASSERT_EQ(expected_bytes_freed, bytes_freed);
     }
+
+    void checkVisibility(bool in_dalvik_cache, int32_t expected_visibility) {
+        int32_t visibility;
+        ASSERT_BINDER_SUCCESS(service_->getOdexVisibility(package_name_, apk_path_, kRuntimeIsa,
+                                                          in_dalvik_cache
+                                                                  ? std::nullopt
+                                                                  : std::make_optional<std::string>(
+                                                                            app_oat_dir_.c_str()),
+                                                          &visibility));
+        EXPECT_EQ(visibility, expected_visibility);
+    }
+
+    void TestGetOdexVisibility(bool in_dalvik_cache) {
+        const char* oat_dir = in_dalvik_cache ? nullptr : app_oat_dir_.c_str();
+
+        checkVisibility(in_dalvik_cache, ODEX_NOT_FOUND);
+
+        CompilePrimaryDexOk("speed-profile",
+                            DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC |
+                                    DEXOPT_GENERATE_APP_IMAGE,
+                            oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
+                            /*binder_result=*/nullptr, empty_dm_file_.c_str());
+        checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC);
+
+        CompilePrimaryDexOk("speed-profile",
+                            DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE,
+                            oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
+                            /*binder_result=*/nullptr, empty_dm_file_.c_str());
+        checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE);
+    }
 };
 
 
@@ -830,6 +860,16 @@
     TestDeleteOdex(/*in_dalvik_cache=*/ true);
 }
 
+TEST_F(DexoptTest, GetOdexVisibilityData) {
+    LOG(INFO) << "GetOdexVisibilityData";
+    TestGetOdexVisibility(/*in_dalvik_cache=*/false);
+}
+
+TEST_F(DexoptTest, GetOdexVisibilityDalvikCache) {
+    LOG(INFO) << "GetOdexVisibilityDalvikCache";
+    TestGetOdexVisibility(/*in_dalvik_cache=*/true);
+}
+
 TEST_F(DexoptTest, ResolveStartupConstStrings) {
     LOG(INFO) << "DexoptDex2oatResolveStartupStrings";
     const std::string property = "persist.device_config.runtime.dex2oat_resolve_startup_strings";
@@ -1105,13 +1145,16 @@
         ASSERT_TRUE(AreFilesEqual(ref_profile_content, ref_profile_));
     }
 
-    // TODO(calin): add dex metadata tests once the ART change is merged.
     void preparePackageProfile(const std::string& package_name, const std::string& profile_name,
-            bool expected_result) {
+                               bool has_dex_metadata, bool has_user_id, bool expected_result) {
         bool result;
-        ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
-                package_name, kTestUserId, kTestAppId, profile_name, apk_path_,
-                /*dex_metadata*/ {}, &result));
+        ASSERT_BINDER_SUCCESS(
+                service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL,
+                                            kTestAppId, profile_name, apk_path_,
+                                            has_dex_metadata ? std::make_optional<std::string>(
+                                                                       empty_dm_file_)
+                                                             : std::nullopt,
+                                            &result));
         ASSERT_EQ(expected_result, result);
 
         if (!expected_result) {
@@ -1119,16 +1162,29 @@
             return;
         }
 
-        std::string code_path_cur_prof = create_current_profile_path(
-                kTestUserId, package_name, profile_name, /*is_secondary_dex*/ false);
-        std::string code_path_ref_profile = create_reference_profile_path(package_name,
-                profile_name, /*is_secondary_dex*/ false);
+        std::string code_path_cur_prof =
+                create_current_profile_path(kTestUserId, package_name, profile_name,
+                                            /*is_secondary_dex*/ false);
+        std::string code_path_ref_profile =
+                create_reference_profile_path(package_name, profile_name,
+                                              /*is_secondary_dex*/ false);
 
-        // Check that we created the current profile.
-        CheckFileAccess(code_path_cur_prof, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+        if (has_user_id) {
+            // Check that we created the current profile.
+            CheckFileAccess(code_path_cur_prof, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+        } else {
+            // Without a user ID, we don't generate a current profile.
+            ASSERT_EQ(-1, access(code_path_cur_prof.c_str(), R_OK));
+        }
 
-        // Without dex metadata we don't generate a reference profile.
-        ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK));
+        if (has_dex_metadata) {
+            int32_t uid = has_user_id ? kTestAppUid : multiuser_get_uid(USER_SYSTEM, kTestAppId);
+            // Check that we created the reference profile.
+            CheckFileAccess(code_path_ref_profile, uid, uid, 0640 | S_IFREG);
+        } else {
+            // Without dex metadata, we don't generate a reference profile.
+            ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK));
+        }
     }
 
   protected:
@@ -1273,12 +1329,32 @@
 
 TEST_F(ProfileTest, ProfilePrepareOk) {
     LOG(INFO) << "ProfilePrepareOk";
-    preparePackageProfile(package_name_, "split.prof", /*expected_result*/ true);
+    preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ true,
+                          /*has_user_id*/ true, /*expected_result*/ true);
+}
+
+TEST_F(ProfileTest, ProfilePrepareOkNoUser) {
+    LOG(INFO) << "ProfilePrepareOk";
+    preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ true,
+                          /*has_user_id*/ false, /*expected_result*/ true);
+}
+
+TEST_F(ProfileTest, ProfilePrepareOkNoDm) {
+    LOG(INFO) << "ProfilePrepareOk";
+    preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ false,
+                          /*has_user_id*/ true, /*expected_result*/ true);
+}
+
+TEST_F(ProfileTest, ProfilePrepareOkNoUserNoDm) {
+    LOG(INFO) << "ProfilePrepareOk";
+    preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ false,
+                          /*has_user_id*/ false, /*expected_result*/ true);
 }
 
 TEST_F(ProfileTest, ProfilePrepareFailInvalidPackage) {
     LOG(INFO) << "ProfilePrepareFailInvalidPackage";
-    preparePackageProfile("not.there.package", "split.prof", /*expected_result*/ false);
+    preparePackageProfile("not.there.package", "split.prof", /*has_dex_metadata*/ true,
+                          /*has_user_id*/ true, /*expected_result*/ false);
 }
 
 TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) {
@@ -1286,7 +1362,8 @@
     SetupProfiles(/*setup_ref*/ false);
     // Change the uid on the profile to trigger a failure.
     ::chown(cur_profile_.c_str(), kTestAppUid + 1, kTestAppGid + 1);
-    preparePackageProfile(package_name_, "primary.prof", /*expected_result*/ false);
+    preparePackageProfile(package_name_, "primary.prof", /*has_dex_metadata*/ true,
+                          /*has_user_id*/ true, /*expected_result*/ false);
 }
 
 
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 672ca7f5..38cb370 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -45,7 +45,7 @@
 #include "utils.h"
 
 using android::base::StringPrintf;
-namespace fs = std::filesystem;
+using std::filesystem::is_empty;
 
 namespace android {
 std::string get_package_name(uid_t uid) {
@@ -79,12 +79,15 @@
 namespace installd {
 
 static constexpr const char* kTestUuid = "TEST";
-static constexpr const char* kTestPath = "/data/local/tmp";
+static const std::string 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;
+static constexpr const int FLAG_CLEAR_CACHE_ONLY = InstalldNativeService::FLAG_CLEAR_CACHE_ONLY;
+static constexpr const int FLAG_CLEAR_CODE_CACHE_ONLY =
+        InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY;
 
 const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
 const gid_t kTestCacheGid = multiuser_get_cache_gid(kTestUserId, kTestAppId);
@@ -111,7 +114,7 @@
 }
 
 static std::string get_full_path(const std::string& path) {
-    return StringPrintf("%s/%s", kTestPath, path.c_str());
+    return StringPrintf("%s/%s", kTestPath.c_str(), path.c_str());
 }
 
 static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
@@ -169,10 +172,9 @@
 }
 
 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);
-                     });
+    return find_file((kTestPath + rootDirectory).c_str(), [](const std::string& name, bool is_dir) {
+        return is_dir && is_renamed_deleted_dir(name);
+    });
 }
 
 class ServiceTest : public testing::Test {
@@ -992,16 +994,12 @@
     }
 
     android::os::ReconcileSdkDataArgs reconcileSdkDataArgs(
-            std::string packageName, std::vector<std::string> codeNames,
-            std::vector<std::string> randomSuffixes) {
+            const std::string& packageName, const std::vector<std::string>& subDirNames) {
         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);
+        for (const auto& subDirName : subDirNames) {
+            args.subDirNames.push_back(subDirName);
         }
         args.userId = kTestUserId;
         args.appId = kTestAppId;
@@ -1051,24 +1049,12 @@
 
     const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo";
     CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
-    CheckFileAccess(fooCePath + "/shared", kTestSdkSandboxUid, kNobodyUid,
-                    S_IFDIR | S_ISGID | 0700);
-    CheckFileAccess(fooCePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
-                    S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess(fooCePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
-                    S_IFDIR | S_ISGID | 0771);
 
     const std::string fooDePath = "misc_de/0/sdksandbox/com.foo";
     CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751);
-    CheckFileAccess(fooDePath + "/shared", kTestSdkSandboxUid, kNobodyUid,
-                    S_IFDIR | S_ISGID | 0700);
-    CheckFileAccess(fooDePath + "/shared/cache", kTestSdkSandboxUid, kTestCacheGid,
-                    S_IFDIR | S_ISGID | 0771);
-    CheckFileAccess(fooDePath + "/shared/code_cache", kTestSdkSandboxUid, kTestCacheGid,
-                    S_IFDIR | S_ISGID | 0771);
 }
 
-TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlag) {
     android::os::CreateAppDataResult result;
     android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
@@ -1080,7 +1066,7 @@
     ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
 }
 
-TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutSdkFlagDeletesExisting) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlagDeletesExisting) {
     android::os::CreateAppDataResult result;
     android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     // Create the app user data.
@@ -1094,7 +1080,7 @@
     ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
 }
 
-TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutDeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutDeFlag) {
     android::os::CreateAppDataResult result;
     android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_SDK;
@@ -1109,7 +1095,7 @@
     ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo"));
 }
 
-TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData_WithoutCeFlag) {
+TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutCeFlag) {
     android::os::CreateAppDataResult result;
     android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
     args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK;
@@ -1126,7 +1112,7 @@
 
 TEST_F(SdkSandboxDataTest, ReconcileSdkData) {
     android::os::ReconcileSdkDataArgs args =
-            reconcileSdkDataArgs("com.foo", {"bar", "baz"}, {"random1", "random2"});
+            reconcileSdkDataArgs("com.foo", {"bar@random1", "baz@random2"});
 
     // Create the sdk data.
     ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
@@ -1160,59 +1146,15 @@
                     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 | S_ISGID | 0700);
-    CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid,
-                    S_IFDIR | S_ISGID | 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"});
+            reconcileSdkDataArgs("com.foo", {"bar@random1", "baz@random2"});
 
     // Create the sdksandbox data.
     ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
 
     // Retry with different package name
-    args.sdkPackageNames[0] = "bar.diff";
+    args.subDirNames[0] = "bar.diff@random1";
 
     // Create the sdksandbox data again
     ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args));
@@ -1272,130 +1214,90 @@
     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"};
+        ASSERT_TRUE(mkdirs(cePackagePath, 0700));
+        ASSERT_TRUE(mkdirs(dePackagePath, 0700));
+        const std::vector<std::string> packagePaths = {cePackagePath, dePackagePath};
+        for (const auto& packagePath : packagePaths) {
+            for (auto sdkName : sdkNames) {
+                ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/cache", 0700));
+                ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/code_cache", 0700));
+                std::ofstream{packagePath + "/" + sdkName + "/cache/cachedTestData.txt"};
+                std::ofstream{packagePath + "/" + 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";
-    // Create the app user data.
-    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
-    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    createTestSdkData("com.foo", {"shared", "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")));
+    ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0,
+                                                FLAG_STORAGE_CE | FLAG_CLEAR_CACHE_ONLY, -1));
+
+    const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo";
+    ASSERT_TRUE(is_empty(packagePath + "/shared/cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache"));
 }
 
 TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCodeCacheFlag) {
-    android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
-    args.packageName = "com.foo";
-    // Create the app user data.
-    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
-    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    createTestSdkData("com.foo", {"shared", "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")));
+    ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0,
+                                                FLAG_STORAGE_CE | FLAG_CLEAR_CODE_CACHE_ONLY, -1));
+
+    const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo";
+    ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache"));
 }
 
 TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCacheFlag) {
-    android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
-    args.packageName = "com.foo";
-    // Create the app user data.
-    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
-    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"});
     // Clear the app user data
     ASSERT_BINDER_SUCCESS(
-            service->clearAppData(args.uuid, args.packageName, args.userId,
+            service->clearAppData(kTestUuid, "com.foo", 0,
                                   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")));
+                                  -1));
+
+    const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo";
+    ASSERT_TRUE(is_empty(packagePath + "/shared/cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache"));
 }
 
 TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCodeCacheFlag) {
-    android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
-    args.packageName = "com.foo";
-    // Create the app user data.
-    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
-    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    createTestSdkData("com.foo", {"shared", "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")));
+    ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0,
+                                                FLAG_STORAGE_DE | FLAG_CLEAR_CODE_CACHE_ONLY, -1));
+
+    const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo";
+    ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache"));
 }
 
 TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndWithoutAnyCacheFlag) {
-    android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
-    args.packageName = "com.foo";
-    // Create the app user data.
-    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
-    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    createTestSdkData("com.foo", {"shared", "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")));
+    ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_CE, -1));
+
+    const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo";
+    ASSERT_TRUE(is_empty(packagePath + "/shared"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk1"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk2"));
 }
 
 TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndWithoutAnyCacheFlag) {
-    android::os::CreateAppDataResult result;
-    android::os::CreateAppDataArgs args = createAppDataArgs("com.foo");
-    args.packageName = "com.foo";
-    // Create the app user data.
-    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
-    createTestSdkData("com.foo", {"sdk1", "sdk2"});
+    createTestSdkData("com.foo", {"shared", "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")));
+    ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_DE, -1));
+
+    const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo";
+    ASSERT_TRUE(is_empty(packagePath + "/shared"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk1"));
+    ASSERT_TRUE(is_empty(packagePath + "/sdk2"));
 }
 
 class DestroyUserDataTest : public SdkSandboxDataTest {};
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 38c1c05..910cd63 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -690,11 +690,11 @@
               create_data_misc_sdk_sandbox_package_path(nullptr, true, 10, "com.foo"));
 
     EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo/shared",
-              create_data_misc_sdk_sandbox_shared_path(nullptr, true, 0, "com.foo"));
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 0, "com.foo", "shared"));
     EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared",
-              create_data_misc_sdk_sandbox_shared_path(nullptr, true, 10, "com.foo"));
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "shared"));
     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"));
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "bar@random"));
 
     // De data paths
     EXPECT_EQ("/data/misc_de/0/sdksandbox",
@@ -707,12 +707,11 @@
               create_data_misc_sdk_sandbox_package_path(nullptr, false, 10, "com.foo"));
 
     EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo/shared",
-              create_data_misc_sdk_sandbox_shared_path(nullptr, false, 0, "com.foo"));
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 0, "com.foo", "shared"));
     EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared",
-              create_data_misc_sdk_sandbox_shared_path(nullptr, false, 10, "com.foo"));
+              create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "shared"));
     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"));
+              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 8cfd123..123e3d4 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -223,28 +223,17 @@
 }
 
 /**
- * Create the path name where shared code data for a particular app will be stored.
- * 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) {
-    return StringPrintf("%s/shared",
-                        create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user,
-                                                                  package_name)
-                                .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>
+ * Create the path name where sdk data for a particular sdk will be stored.
+ * E.g. /data/misc_ce/0/sdksandbox/<package-name>/com.foo@randomstrings
  */
 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);
+                                                  const char* sub_dir_name) {
+    return StringPrintf("%s/%s",
+                        create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user,
+                                                                  package_name)
+                                .c_str(),
+                        sub_dir_name);
 }
 
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) {
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 54d77f9..cb30993 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -65,11 +65,9 @@
                                               userid_t userid);
 std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData,
                                                       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);
+                                                  const char* sub_dir_name);
 
 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);
diff --git a/include/android/storage_manager.h b/include/android/storage_manager.h
index 7f2ee08..270570e 100644
--- a/include/android/storage_manager.h
+++ b/include/android/storage_manager.h
@@ -124,6 +124,12 @@
 
 /**
  * Attempts to mount an OBB file. This is an asynchronous operation.
+ *
+ * Since API level 33, this function can only be used to mount unencrypted OBBs,
+ * i.e. the {@code key} parameter must be {@code null} or an empty string. Note
+ * that even before API level 33, mounting encrypted OBBs didn't work on many
+ * Android device implementations. Applications should not assume any particular
+ * behavior when {@code key} is nonempty.
  */
 void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key,
         AStorageManager_obbCallbackFunc cb, void* data);
diff --git a/libs/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h
index fdd532a..57e03cb 100644
--- a/libs/binder/BufferedTextOutput.h
+++ b/libs/binder/BufferedTextOutput.h
@@ -18,8 +18,8 @@
 #define ANDROID_BUFFEREDTEXTOUTPUT_H
 
 #include <binder/TextOutput.h>
-#include <utils/threads.h>
 #include <sys/uio.h>
+#include <utils/Mutex.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 9c7ff97..c6b0cb7 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -31,7 +31,7 @@
 #include <binder/Parcel.h>
 #include <log/log.h>
 
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 
 #include <map>
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index f79075d..3c97dca 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -27,7 +27,6 @@
 #include <utils/CallStack.h>
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
-#include <utils/threads.h>
 
 #include <atomic>
 #include <errno.h>
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index c4475c7..03553f3 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -23,7 +23,6 @@
 #include <utils/Log.h>
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
-#include <utils/threads.h>
 
 #include <stdint.h>
 #include <stdio.h>
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 504c6c2..a217a15 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1862,6 +1862,7 @@
 {
     status_t status = readNullableStrongBinder(val);
     if (status == OK && !val->get()) {
+        ALOGW("Expecting binder but got null!");
         status = UNEXPECTED_NULL;
     }
     return status;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index b14a838..4a01d81 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -25,9 +25,10 @@
 #include <binder/IServiceManager.h>
 #include <binder/Stability.h>
 #include <cutils/atomic.h>
+#include <utils/AndroidThreads.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
-#include <utils/threads.h>
+#include <utils/Thread.h>
 
 #include "Static.h"
 #include "binder_module.h"
diff --git a/libs/binder/Static.h b/libs/binder/Static.h
index 83524e8..8444fe7 100644
--- a/libs/binder/Static.h
+++ b/libs/binder/Static.h
@@ -17,8 +17,6 @@
 // All static variables go here, to control initialization and
 // destruction order in the library.
 
-#include <utils/threads.h>
-
 #include <binder/IBinder.h>
 #include <binder/ProcessState.h>
 
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 8deb2fe..19ad5e6 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -18,7 +18,6 @@
 
 #include <binder/IBinder.h>
 #include <utils/Mutex.h>
-#include <utils/threads.h>
 
 #include <map>
 #include <unordered_map>
diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h
index e658574..6f9eb5e 100644
--- a/libs/binder/include/binder/PermissionController.h
+++ b/libs/binder/include/binder/PermissionController.h
@@ -19,8 +19,7 @@
 #ifndef __ANDROID_VNDK__
 
 #include <binder/IPermissionController.h>
-
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 0deee73..675585e 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -18,11 +18,10 @@
 
 #include <binder/IBinder.h>
 #include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
 
-#include <utils/threads.h>
-
 #include <pthread.h>
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
index b772b80..abc7f1d 100644
--- a/libs/binder/include_activitymanager/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -18,10 +18,9 @@
 
 #ifndef __ANDROID_VNDK__
 
-#include <binder/IActivityManager.h>
 #include <android/app/ProcessStateEnum.h>
-
-#include <utils/threads.h>
+#include <binder/IActivityManager.h>
+#include <utils/Mutex.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 467e51e..5d6206d 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1086,7 +1086,7 @@
     {
         $( #[$attr:meta] )*
         $enum:ident : [$backing:ty; $size:expr] {
-            $( $name:ident = $value:expr, )*
+            $( $( #[$value_attr:meta] )* $name:ident = $value:expr, )*
         }
     } => {
         $( #[$attr] )*
@@ -1094,7 +1094,7 @@
         #[allow(missing_docs)]
         pub struct $enum(pub $backing);
         impl $enum {
-            $( #[allow(missing_docs)] pub const $name: Self = Self($value); )*
+            $( $( #[$value_attr] )* #[allow(missing_docs)] pub const $name: Self = Self($value); )*
 
             #[inline(always)]
             #[allow(missing_docs)]
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index 749bf21..633626c 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -19,13 +19,18 @@
 #include <binder/Parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <functional>
+
 namespace android {
 /**
  * Fill parcel data, including some random binder objects and FDs
+ *
+ * p - the Parcel to fill
+ * provider - takes ownership and completely consumes provider
+ * writeHeader - optional function to write a specific header once the format of the parcel is
+ *     picked (for instance, to write an interface header)
  */
-void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider);
-/**
- * Fill parcel data, but don't fill any objects.
- */
-void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider);
+void fillRandomParcel(
+        Parcel* p, FuzzedDataProvider&& provider,
+        std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader = nullptr);
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index e849c9b..be39bb9 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -27,7 +27,14 @@
 
         std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
                 provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
-        fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()));
+        fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()),
+                         [&binder](Parcel* p, FuzzedDataProvider& provider) {
+                             // most code will be behind checks that the head of the Parcel
+                             // is exactly this, so make it easier for fuzzers to reach this
+                             if (provider.ConsumeBool()) {
+                                 p->writeInterfaceToken(binder->getInterfaceDescriptor());
+                             }
+                         });
 
         Parcel reply;
         (void)binder->transact(code, data, &reply, flags);
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 8bf04cc..cfabc1e 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -34,15 +34,26 @@
     String16 mDescriptor;
 };
 
-void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
+static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
+    std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes());
+    CHECK(OK == p->write(data.data(), data.size()));
+}
+
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider,
+                      std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader) {
     if (provider.ConsumeBool()) {
         auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
         CHECK_EQ(OK, session->addNullDebuggingClient());
         p->markForRpc(session);
+
+        writeHeader(p, provider);
+
         fillRandomParcelData(p, std::move(provider));
         return;
     }
 
+    writeHeader(p, provider);
+
     while (provider.remaining_bytes() > 0) {
         auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
                 // write data
@@ -85,9 +96,4 @@
     }
 }
 
-void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
-    std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes());
-    CHECK(OK == p->write(data.data(), data.size()));
-}
-
 } // namespace android
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
index 741987f..5079431 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
@@ -30,7 +30,6 @@
 #include <utils/KeyedVector.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
-#include <utils/threads.h>
 
 #include <stdio.h>
 
diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
index 4a0aeba..bf7c613 100644
--- a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
@@ -27,7 +27,6 @@
 #include <utils/KeyedVector.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
-#include <utils/threads.h>
 
 namespace android {
 
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index ec4c7c1..c2793ac 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -184,6 +184,10 @@
     mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
     // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
     t.setApplyToken(mApplyToken).apply(false, true);
+
+    if (mTransactionReadyCallback) {
+        mTransactionReadyCallback(mSyncTransaction);
+    }
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
@@ -702,14 +706,31 @@
         std::function<void(SurfaceComposerClient::Transaction*)> callback,
         bool acquireSingleBuffer) {
     BBQ_TRACE();
-    std::lock_guard _lock{mMutex};
-    mTransactionReadyCallback = callback;
-    if (callback) {
-        mSyncTransaction = new SurfaceComposerClient::Transaction();
-    } else {
-        mSyncTransaction = nullptr;
+
+    std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
+    SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+
+    {
+        std::lock_guard _lock{mMutex};
+        // We're about to overwrite the previous call so we should invoke that callback
+        // immediately.
+        if (mTransactionReadyCallback) {
+            prevCallback = mTransactionReadyCallback;
+            prevTransaction = mSyncTransaction;
+        }
+
+        mTransactionReadyCallback = callback;
+        if (callback) {
+            mSyncTransaction = new SurfaceComposerClient::Transaction();
+        } else {
+            mSyncTransaction = nullptr;
+        }
+        mAcquireSingleBuffer = mTransactionReadyCallback ? acquireSingleBuffer : true;
     }
-    mAcquireSingleBuffer = mTransactionReadyCallback ? acquireSingleBuffer : true;
+
+    if (prevCallback) {
+        prevCallback(prevTransaction);
+    }
 }
 
 void BLASTBufferQueue::stopContinuousSyncTransaction() {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 338ff11..49b669e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -594,6 +594,10 @@
         what |= eColorChanged;
         color = other.color;
     }
+    if (other.what & eColorSpaceAgnosticChanged) {
+        what |= eColorSpaceAgnosticChanged;
+        colorSpaceAgnostic = other.colorSpaceAgnostic;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 27856ce..52a22a7 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1421,7 +1421,7 @@
 
 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,
+        const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& optFrameNumber,
         ReleaseBufferCallback callback) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
@@ -1433,10 +1433,9 @@
 
     std::shared_ptr<BufferData> bufferData = std::make_shared<BufferData>();
     bufferData->buffer = buffer;
-    if (frameNumber) {
-        bufferData->frameNumber = *frameNumber;
-        bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
-    }
+    uint64_t frameNumber = sc->resolveFrameNumber(optFrameNumber);
+    bufferData->frameNumber = frameNumber;
+    bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
     if (fence) {
         bufferData->acquireFence = *fence;
         bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
@@ -2320,9 +2319,11 @@
 }
 
 status_t SurfaceComposerClient::addWindowInfosListener(
-        const sp<WindowInfosListener>& windowInfosListener) {
+        const sp<WindowInfosListener>& windowInfosListener,
+        std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>>* outInitialInfo) {
     return WindowInfosListenerReporter::getInstance()
-            ->addWindowInfosListener(windowInfosListener, ComposerService::getComposerService());
+            ->addWindowInfosListener(windowInfosListener, ComposerService::getComposerService(),
+                                     outInitialInfo);
 }
 
 status_t SurfaceComposerClient::removeWindowInfosListener(
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 6529a4e..063dda5 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -280,5 +280,18 @@
     return this;
 }
 
+uint64_t SurfaceControl::resolveFrameNumber(const std::optional<uint64_t>& frameNumber) {
+    if (frameNumber.has_value()) {
+        auto ret = frameNumber.value();
+        // Set the fallback to something far enough ahead that in the unlikely event of mixed
+        // "real" frame numbers and fallback frame numbers, we still won't collide in any
+        // meaningful capacity
+        mFallbackFrameNumber = ret + 100;
+        return ret;
+    } else {
+        return mFallbackFrameNumber++;
+    }
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 4112f74..cfc7dbc 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -31,7 +31,8 @@
 
 status_t WindowInfosListenerReporter::addWindowInfosListener(
         const sp<WindowInfosListener>& windowInfosListener,
-        const sp<ISurfaceComposer>& surfaceComposer) {
+        const sp<ISurfaceComposer>& surfaceComposer,
+        std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>>* outInitialInfo) {
     status_t status = OK;
     {
         std::scoped_lock lock(mListenersMutex);
@@ -42,6 +43,11 @@
         if (status == OK) {
             mWindowInfosListeners.insert(windowInfosListener);
         }
+
+        if (outInitialInfo != nullptr) {
+            outInitialInfo->first = mLastWindowInfos;
+            outInitialInfo->second = mLastDisplayInfos;
+        }
     }
 
     return status;
@@ -55,6 +61,10 @@
         std::scoped_lock lock(mListenersMutex);
         if (mWindowInfosListeners.size() == 1) {
             status = surfaceComposer->removeWindowInfosListener(this);
+            // Clear the last stored state since we're disabling updates and don't want to hold
+            // stale values
+            mLastWindowInfos.clear();
+            mLastDisplayInfos.clear();
         }
 
         if (status == OK) {
@@ -75,6 +85,9 @@
         for (auto listener : mWindowInfosListeners) {
             windowInfosListeners.insert(listener);
         }
+
+        mLastWindowInfos = windowInfos;
+        mLastDisplayInfos = displayInfos;
     }
 
     for (auto listener : windowInfosListeners) {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index c8ac166..9d03f58 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -696,7 +696,10 @@
     static status_t removeTunnelModeEnabledListener(
             const sp<gui::ITunnelModeEnabledListener>& listener);
 
-    status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+    status_t addWindowInfosListener(
+            const sp<gui::WindowInfosListener>& windowInfosListener,
+            std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>>* outInitialInfo =
+                    nullptr);
     status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
 
 protected:
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 9ee4636..1690e44 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <optional>
 
 #include <utils/RefBase.h>
 #include <utils/threads.h>
@@ -98,6 +99,8 @@
 
     sp<SurfaceControl> getParentingLayer();
 
+    uint64_t resolveFrameNumber(const std::optional<uint64_t>& frameNumber);
+
 private:
     // can't be copied
     SurfaceControl& operator = (SurfaceControl& rhs);
@@ -124,6 +127,7 @@
     uint32_t mHeight;
     PixelFormat mFormat;
     uint32_t mCreateFlags;
+    uint64_t mFallbackFrameNumber = 100;
 };
 
 }; // namespace android
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 96bd0b1..3b4aed4 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -34,15 +34,19 @@
                                         const std::vector<gui::DisplayInfo>&,
                                         const sp<gui::IWindowInfosReportedListener>&) override;
 
-    status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
-                                    const sp<ISurfaceComposer>&);
+    status_t addWindowInfosListener(
+            const sp<gui::WindowInfosListener>& windowInfosListener, const sp<ISurfaceComposer>&,
+            std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>>* outInitialInfo);
     status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
-                                       const sp<ISurfaceComposer>&);
+                                       const sp<ISurfaceComposer>& surfaceComposer);
     void reconnect(const sp<ISurfaceComposer>&);
 
 private:
     std::mutex mListenersMutex;
     std::unordered_set<sp<gui::WindowInfosListener>, SpHash<gui::WindowInfosListener>>
             mWindowInfosListeners GUARDED_BY(mListenersMutex);
+
+    std::vector<gui::WindowInfo> mLastWindowInfos GUARDED_BY(mListenersMutex);
+    std::vector<gui::DisplayInfo> mLastDisplayInfos GUARDED_BY(mListenersMutex);
 };
 } // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 0c3236c..cb7e94c 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -1083,6 +1083,34 @@
             checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
 
+TEST_F(BLASTBufferQueueTest, SyncNextTransactionOverwrite) {
+    std::mutex mutex;
+    std::condition_variable callbackReceivedCv;
+    bool receivedCallback = false;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback());
+    auto callback = [&](Transaction*) {
+        std::unique_lock<std::mutex> lock(mutex);
+        receivedCallback = true;
+        callbackReceivedCv.notify_one();
+    };
+    adapter.syncNextTransaction(callback);
+    ASSERT_NE(nullptr, adapter.getTransactionReadyCallback());
+
+    auto callback2 = [](Transaction*) {};
+    adapter.syncNextTransaction(callback2);
+
+    std::unique_lock<std::mutex> lock(mutex);
+    if (!receivedCallback) {
+        ASSERT_NE(callbackReceivedCv.wait_for(lock, std::chrono::seconds(3)),
+                  std::cv_status::timeout)
+                << "did not receive callback";
+    }
+
+    ASSERT_TRUE(receivedCallback);
+}
+
 // This test will currently fail because the old surfacecontrol will steal the last presented buffer
 // until the old surface control is destroyed. This is not necessarily a bug but to document a
 // limitation with the update API and to test any changes to make the api more robust. The current
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 606fe2a..18fb7c1 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -112,7 +112,7 @@
                 "frameworks/native/libs/arect/include",
             ],
         },
-        linux_glibc: {
+        host_linux: {
             srcs: [
                 "InputTransport.cpp",
                 "android/os/IInputConstants.aidl",
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index b467b35..5e5618b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -654,10 +654,14 @@
         colorTransform *=
                 mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
                                  parameters.layerDimmingRatio, 1.f));
+        const auto targetBuffer = parameters.layer.source.buffer.buffer;
+        const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+        const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr;
         return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
                                         parameters.display.maxLuminance,
                                         parameters.display.currentLuminanceNits,
-                                        parameters.layer.source.buffer.maxLuminanceNits);
+                                        parameters.layer.source.buffer.maxLuminanceNits,
+                                        hardwareBuffer);
     }
     return parameters.shader;
 }
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index a46329d..d479606 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -44,7 +44,8 @@
                                          const shaders::LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float currentDisplayLuminanceNits, float maxLuminance) {
+                                         float currentDisplayLuminanceNits, float maxLuminance,
+                                         AHardwareBuffer* buffer) {
     ATRACE_CALL();
     SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
 
@@ -52,7 +53,7 @@
 
     const auto uniforms =
             shaders::buildLinearEffectUniforms(linearEffect, colorTransform, maxDisplayLuminance,
-                                               currentDisplayLuminanceNits, maxLuminance);
+                                               currentDisplayLuminanceNits, maxLuminance, buffer);
 
     for (const auto& uniform : uniforms) {
         effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
@@ -63,4 +64,4 @@
 
 } // namespace skia
 } // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index e0a556b..26bae3b 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -40,11 +40,14 @@
 // * The current luminance of the physical display in nits
 // * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086
 // or as the max light level from the CTA 861.3 standard.
+// * An AHardwareBuffer for implementations that support gralloc4 metadata for
+// communicating any HDR metadata.
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
                                          const shaders::LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float currentDisplayLuminanceNits, float maxLuminance);
+                                         float currentDisplayLuminanceNits, float maxLuminance,
+                                         AHardwareBuffer* buffer);
 } // namespace skia
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index add7a94..38ae2fd 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -1562,15 +1562,21 @@
         const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB;
 
         const vec3 scaledXYZ = scaleOotf(xyz, kCurrentLuminanceNits);
-        const double gain =
+        const auto gains =
                 tonemap::getToneMapper()
                         ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common::
                                                                 Dataspace>(sourceDataspace),
                                             static_cast<aidl::android::hardware::graphics::common::
                                                                 Dataspace>(
                                                     ui::Dataspace::DISPLAY_P3),
-                                            scaleOotf(linearRGB, kCurrentLuminanceNits), scaledXYZ,
+                                            {tonemap::
+                                                     Color{.linearRGB =
+                                                                   scaleOotf(linearRGB,
+                                                                             kCurrentLuminanceNits),
+                                                           .xyz = scaledXYZ}},
                                             metadata);
+        EXPECT_EQ(1, gains.size());
+        const double gain = gains.front();
         const vec3 normalizedXYZ = scaledXYZ * gain / metadata.displayMaxLuminance;
 
         const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * normalizedXYZ) * 255;
@@ -2613,8 +2619,7 @@
             [](vec3 color) { return EOTF_HLG(color); },
             [](vec3 color, float currentLuminaceNits) {
                 static constexpr float kMaxHLGLuminance = 1000.f;
-                static const float kHLGGamma = 1.2 + 0.42 * std::log10(currentLuminaceNits / 1000);
-                return color * kMaxHLGLuminance * std::pow(color.y, kHLGGamma - 1);
+                return color * kMaxHLGLuminance;
             });
 }
 
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
index 1cd143e..2f8bf49 100644
--- a/libs/shaders/Android.bp
+++ b/libs/shaders/Android.bp
@@ -30,9 +30,11 @@
     shared_libs: [
         "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.common@1.2",
+        "libnativewindow",
     ],
 
     static_libs: [
+        "libarect",
         "libmath",
         "libtonemap",
         "libui-types",
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
index 43828cc..4ec7594 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -101,6 +101,7 @@
                                                               const mat4& colorTransform,
                                                               float maxDisplayLuminance,
                                                               float currentDisplayLuminanceNits,
-                                                              float maxLuminance);
+                                                              float maxLuminance,
+                                                              AHardwareBuffer* buffer = nullptr);
 
 } // namespace android::shaders
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index 03da3ec..5935589 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -185,9 +185,8 @@
             break;
         case HAL_DATASPACE_TRANSFER_HLG:
             shader.append(R"(
-                    uniform float in_hlgGamma;
                     float3 ScaleLuminance(float3 xyz) {
-                        return xyz * 1000.0 * pow(xyz.y, in_hlgGamma - 1);
+                        return xyz * 1000.0;
                     }
                 )");
             break;
@@ -228,10 +227,8 @@
             break;
         case HAL_DATASPACE_TRANSFER_HLG:
             shader.append(R"(
-                    uniform float in_hlgGamma;
                     float3 NormalizeLuminance(float3 xyz) {
-                        return xyz / 1000.0 *
-                                pow(xyz.y / 1000.0, (1 - in_hlgGamma) / (in_hlgGamma));
+                        return xyz / 1000.0;
                     }
                 )");
             break;
@@ -451,11 +448,6 @@
     return result;
 }
 
-// Refer to BT2100-2
-float computeHlgGamma(float currentDisplayBrightnessNits) {
-    return 1.2 + 0.42 * std::log10(currentDisplayBrightnessNits / 1000);
-}
-
 } // namespace
 
 std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
@@ -476,7 +468,8 @@
                                                               const mat4& colorTransform,
                                                               float maxDisplayLuminance,
                                                               float currentDisplayLuminanceNits,
-                                                              float maxLuminance) {
+                                                              float maxLuminance,
+                                                              AHardwareBuffer* buffer) {
     std::vector<tonemap::ShaderUniform> uniforms;
     if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
         uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())});
@@ -492,19 +485,17 @@
                                     colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
     }
 
-    if ((linearEffect.inputDataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_HLG) {
-        uniforms.push_back(
-                {.name = "in_hlgGamma",
-                 .value = buildUniformValue<float>(computeHlgGamma(currentDisplayLuminanceNits))});
-    }
-
     tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
                                // If the input luminance is unknown, use display luminance (aka,
                                // no-op any luminance changes)
                                // This will be the case for eg screenshots in addition to
                                // uncalibrated displays
                                .contentMaxLuminance =
-                                       maxLuminance > 0 ? maxLuminance : maxDisplayLuminance};
+                                       maxLuminance > 0 ? maxLuminance : maxDisplayLuminance,
+                               .currentDisplayLuminance = currentDisplayLuminanceNits > 0
+                                       ? currentDisplayLuminanceNits
+                                       : maxDisplayLuminance,
+                               .buffer = buffer};
 
     for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
         uniforms.push_back(uniform);
@@ -513,4 +504,4 @@
     return uniforms;
 }
 
-} // namespace android::shaders
\ No newline at end of file
+} // namespace android::shaders
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
index 99d1b22..dc55586 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -30,9 +30,11 @@
     shared_libs: [
         "android.hardware.graphics.common-V3-ndk",
         "liblog",
+        "libnativewindow",
     ],
 
     static_libs: [
+        "libarect",
         "libmath",
     ],
 
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
index b9abf8c..c51016d 100644
--- a/libs/tonemap/include/tonemap/tonemap.h
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <android/hardware_buffer.h>
 #include <math/vec3.h>
 
 #include <string>
@@ -44,8 +45,30 @@
 struct Metadata {
     // The maximum luminance of the display in nits
     float displayMaxLuminance = 0.0;
+
     // The maximum luminance of the content in nits
     float contentMaxLuminance = 0.0;
+
+    // The current brightness of the display in nits
+    float currentDisplayLuminance = 0.0;
+
+    // Reference to an AHardwareBuffer.
+    // Devices that support gralloc 4.0 and higher may attach metadata onto a
+    // particular frame's buffer, including metadata used by HDR-standards like
+    // SMPTE 2086 or SMPTE 2094-40.
+    // Note that this parameter may be optional if there is no hardware buffer
+    // available, for instance if the source content is generated from a GL
+    // texture that does not have associated metadata. As such, implementations
+    // must support nullptr.
+    AHardwareBuffer* buffer = nullptr;
+};
+
+// Utility class containing pre-processed conversions for a particular color
+struct Color {
+    // RGB color in linear space
+    vec3 linearRGB;
+    // CIE 1931 XYZ representation of the color
+    vec3 xyz;
 };
 
 class ToneMapper {
@@ -108,14 +131,15 @@
     // described by destinationDataspace. To compute the gain, the input colors are provided by
     // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also
     // provided. Metadata is also provided for helping to compute the tonemapping curve.
-    virtual double lookupTonemapGain(
+    using Gain = double;
+    virtual std::vector<Gain> lookupTonemapGain(
             aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
             aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
-            vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0;
+            const std::vector<Color>& colors, const Metadata& metadata) = 0;
 };
 
 // Retrieves a tonemapper instance.
 // This instance is globally constructed.
 ToneMapper* getToneMapper();
 
-} // namespace android::tonemap
\ No newline at end of file
+} // namespace android::tonemap
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index d519482..26a1d79 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -32,6 +32,7 @@
     ],
     shared_libs: [
         "android.hardware.graphics.common-V3-ndk",
+        "libnativewindow",
     ],
     static_libs: [
         "libmath",
diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp
index bc0a884..19e1eea 100644
--- a/libs/tonemap/tonemap.cpp
+++ b/libs/tonemap/tonemap.cpp
@@ -48,6 +48,22 @@
     return result;
 }
 
+// Refer to BT2100-2
+float computeHlgGamma(float currentDisplayBrightnessNits) {
+    // BT 2100-2's recommendation for taking into account the nominal max
+    // brightness of the display does not work when the current brightness is
+    // very low. For instance, the gamma becomes negative when the current
+    // brightness is between 1 and 2 nits, which would be a bad experience in a
+    // dark environment. Furthermore, BT2100-2 recommends applying
+    // channel^(gamma - 1) as its OOTF, which means that when the current
+    // brightness is lower than 335 nits then channel * channel^(gamma - 1) >
+    // channel, which makes dark scenes very bright. As a workaround for those
+    // problems, lower-bound the brightness to 500 nits.
+    constexpr float minBrightnessNits = 500.f;
+    currentDisplayBrightnessNits = std::max(minBrightnessNits, currentDisplayBrightnessNits);
+    return 1.2 + 0.42 * std::log10(currentDisplayBrightnessNits / 1000);
+}
+
 class ToneMapperO : public ToneMapper {
 public:
     std::string generateTonemapGainShaderSkSL(
@@ -79,11 +95,27 @@
                         // HLG output.
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(vec3 xyz) {
-                                        return clamp(xyz.y, 0.0, 1000.0);
+                                        float nits = clamp(xyz.y, 0.0, 1000.0);
+                                        return nits * pow(nits / 1000.0, -0.2 / 1.2);
                                     }
                                 )");
                         break;
                     default:
+                        // HLG follows BT2100, but this tonemapping version
+                        // does not take into account current display brightness
+                        if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return pow(nits, 0.2);
+                                        }
+                                    )");
+                        } else {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return 1.0;
+                                        }
+                                    )");
+                        }
                         // Here we're mapping from HDR to SDR content, so interpolate using a
                         // Hermitian polynomial onto the smaller luminance range.
                         program.append(R"(
@@ -91,6 +123,8 @@
                                         float maxInLumi = in_libtonemap_inputMaxLuminance;
                                         float maxOutLumi = in_libtonemap_displayMaxLuminance;
 
+                                        xyz = xyz * libtonemap_applyBaseOOTFGain(xyz.y);
+
                                         float nits = xyz.y;
 
                                         // if the max input luminance is less than what we can
@@ -153,6 +187,21 @@
                 switch (destinationDataspaceInt & kTransferMask) {
                     case kTransferST2084:
                     case kTransferHLG:
+                        // HLG follows BT2100, but this tonemapping version
+                        // does not take into account current display brightness
+                        if ((destinationDataspaceInt & kTransferMask) == kTransferHLG) {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return pow(nits / 1000.0, -0.2 / 1.2);
+                                        }
+                                    )");
+                        } else {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return 1.0;
+                                        }
+                                    )");
+                        }
                         // Map from SDR onto an HDR output buffer
                         // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
                         // [0, maxOutLumi] which is hard-coded to be 3000 nits.
@@ -178,7 +227,7 @@
                                         if (nits <= x0) {
                                             // scale [0.0, x0] to [0.0, y0] linearly
                                             float slope = y0 / x0;
-                                            return nits * slope;
+                                            nits = nits * slope;
                                         } else if (nits <= x1) {
                                             // scale [x0, x1] to [y0, y1] using a curve
                                             float t = (nits - x0) / (x1 - x0);
@@ -196,7 +245,7 @@
                                                     2.0 * (1.0 - t) * t * c3 + t * t * y3;
                                         }
 
-                                        return nits;
+                                        return nits * libtonemap_applyBaseOOTFGain(nits);
                                     }
                                 )");
                         break;
@@ -236,136 +285,152 @@
         return uniforms;
     }
 
-    double lookupTonemapGain(
+    std::vector<Gain> lookupTonemapGain(
             aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
             aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
-            vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override {
-        if (xyz.y <= 0.0) {
-            return 1.0;
-        }
-        const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
-        const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+            const std::vector<Color>& colors, const Metadata& metadata) override {
+        std::vector<Gain> gains;
+        gains.reserve(colors.size());
 
-        double targetNits = 0.0;
-        switch (sourceDataspaceInt & kTransferMask) {
-            case kTransferST2084:
-            case kTransferHLG:
-                switch (destinationDataspaceInt & kTransferMask) {
-                    case kTransferST2084:
-                        targetNits = xyz.y;
-                        break;
-                    case kTransferHLG:
-                        // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
-                        // we'll clamp the luminance range in case we're mapping from PQ input to
-                        // HLG output.
-                        targetNits = std::clamp(xyz.y, 0.0f, 1000.0f);
-                        break;
-                    default:
-                        // Here we're mapping from HDR to SDR content, so interpolate using a
-                        // Hermitian polynomial onto the smaller luminance range.
+        for (const auto [_, xyz] : colors) {
+            if (xyz.y <= 0.0) {
+                gains.push_back(1.0);
+                continue;
+            }
+            const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+            const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
 
-                        targetNits = xyz.y;
-                        // if the max input luminance is less than what we can output then
-                        // no tone mapping is needed as all color values will be in range.
-                        if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) {
-                            // three control points
-                            const double x0 = 10.0;
-                            const double y0 = 17.0;
-                            double x1 = metadata.displayMaxLuminance * 0.75;
-                            double y1 = x1;
-                            double x2 = x1 + (metadata.contentMaxLuminance - x1) / 2.0;
-                            double y2 = y1 + (metadata.displayMaxLuminance - y1) * 0.75;
+            double targetNits = 0.0;
+            switch (sourceDataspaceInt & kTransferMask) {
+                case kTransferST2084:
+                case kTransferHLG:
+                    switch (destinationDataspaceInt & kTransferMask) {
+                        case kTransferST2084:
+                            targetNits = xyz.y;
+                            break;
+                        case kTransferHLG:
+                            // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG,
+                            // so we'll clamp the luminance range in case we're mapping from PQ
+                            // input to HLG output.
+                            targetNits = std::clamp(xyz.y, 0.0f, 1000.0f);
+                            targetNits *= std::pow(targetNits / 1000.f, -0.2 / 1.2);
+                            break;
+                        default:
+                            // Here we're mapping from HDR to SDR content, so interpolate using a
+                            // Hermitian polynomial onto the smaller luminance range.
 
-                            // horizontal distances between the last three control points
-                            double h12 = x2 - x1;
-                            double h23 = metadata.contentMaxLuminance - x2;
-                            // tangents at the last three control points
-                            double m1 = (y2 - y1) / h12;
-                            double m3 = (metadata.displayMaxLuminance - y2) / h23;
-                            double m2 = (m1 + m3) / 2.0;
+                            targetNits = xyz.y;
 
-                            if (targetNits < x0) {
+                            if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+                                targetNits *= std::pow(targetNits, 0.2);
+                            }
+                            // if the max input luminance is less than what we can output then
+                            // no tone mapping is needed as all color values will be in range.
+                            if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) {
+                                // three control points
+                                const double x0 = 10.0;
+                                const double y0 = 17.0;
+                                double x1 = metadata.displayMaxLuminance * 0.75;
+                                double y1 = x1;
+                                double x2 = x1 + (metadata.contentMaxLuminance - x1) / 2.0;
+                                double y2 = y1 + (metadata.displayMaxLuminance - y1) * 0.75;
+
+                                // horizontal distances between the last three control points
+                                double h12 = x2 - x1;
+                                double h23 = metadata.contentMaxLuminance - x2;
+                                // tangents at the last three control points
+                                double m1 = (y2 - y1) / h12;
+                                double m3 = (metadata.displayMaxLuminance - y2) / h23;
+                                double m2 = (m1 + m3) / 2.0;
+
+                                if (targetNits < x0) {
+                                    // scale [0.0, x0] to [0.0, y0] linearly
+                                    double slope = y0 / x0;
+                                    targetNits *= slope;
+                                } else if (targetNits < x1) {
+                                    // scale [x0, x1] to [y0, y1] linearly
+                                    double slope = (y1 - y0) / (x1 - x0);
+                                    targetNits = y0 + (targetNits - x0) * slope;
+                                } else if (targetNits < x2) {
+                                    // scale [x1, x2] to [y1, y2] using Hermite interp
+                                    double t = (targetNits - x1) / h12;
+                                    targetNits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) *
+                                                    (1.0 - t) +
+                                            (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+                                } else {
+                                    // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite
+                                    // interp
+                                    double t = (targetNits - x2) / h23;
+                                    targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) *
+                                                    (1.0 - t) +
+                                            (metadata.displayMaxLuminance * (3.0 - 2.0 * t) +
+                                             h23 * m3 * (t - 1.0)) *
+                                                    t * t;
+                                }
+                            }
+                            break;
+                    }
+                    break;
+                default:
+                    // source is SDR
+                    switch (destinationDataspaceInt & kTransferMask) {
+                        case kTransferST2084:
+                        case kTransferHLG: {
+                            // Map from SDR onto an HDR output buffer
+                            // Here we use a polynomial curve to map from [0, displayMaxLuminance]
+                            // onto [0, maxOutLumi] which is hard-coded to be 3000 nits.
+                            const double maxOutLumi = 3000.0;
+
+                            double x0 = 5.0;
+                            double y0 = 2.5;
+                            double x1 = metadata.displayMaxLuminance * 0.7;
+                            double y1 = maxOutLumi * 0.15;
+                            double x2 = metadata.displayMaxLuminance * 0.9;
+                            double y2 = maxOutLumi * 0.45;
+                            double x3 = metadata.displayMaxLuminance;
+                            double y3 = maxOutLumi;
+
+                            double c1 = y1 / 3.0;
+                            double c2 = y2 / 2.0;
+                            double c3 = y3 / 1.5;
+
+                            targetNits = xyz.y;
+
+                            if (targetNits <= x0) {
                                 // scale [0.0, x0] to [0.0, y0] linearly
                                 double slope = y0 / x0;
                                 targetNits *= slope;
-                            } else if (targetNits < x1) {
-                                // scale [x0, x1] to [y0, y1] linearly
-                                double slope = (y1 - y0) / (x1 - x0);
-                                targetNits = y0 + (targetNits - x0) * slope;
-                            } else if (targetNits < x2) {
-                                // scale [x1, x2] to [y1, y2] using Hermite interp
-                                double t = (targetNits - x1) / h12;
-                                targetNits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) *
-                                                (1.0 - t) +
-                                        (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+                            } else if (targetNits <= x1) {
+                                // scale [x0, x1] to [y0, y1] using a curve
+                                double t = (targetNits - x0) / (x1 - x0);
+                                targetNits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 +
+                                        t * t * y1;
+                            } else if (targetNits <= x2) {
+                                // scale [x1, x2] to [y1, y2] using a curve
+                                double t = (targetNits - x1) / (x2 - x1);
+                                targetNits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 +
+                                        t * t * y2;
                             } else {
-                                // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
-                                double t = (targetNits - x2) / h23;
-                                targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) *
-                                                (1.0 - t) +
-                                        (metadata.displayMaxLuminance * (3.0 - 2.0 * t) +
-                                         h23 * m3 * (t - 1.0)) *
-                                                t * t;
+                                // scale [x2, x3] to [y2, y3] using a curve
+                                double t = (targetNits - x2) / (x3 - x2);
+                                targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 +
+                                        t * t * y3;
                             }
-                        }
-                        break;
-                }
-                break;
-            default:
-                // source is SDR
-                switch (destinationDataspaceInt & kTransferMask) {
-                    case kTransferST2084:
-                    case kTransferHLG: {
-                        // Map from SDR onto an HDR output buffer
-                        // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
-                        // [0, maxOutLumi] which is hard-coded to be 3000 nits.
-                        const double maxOutLumi = 3000.0;
 
-                        double x0 = 5.0;
-                        double y0 = 2.5;
-                        double x1 = metadata.displayMaxLuminance * 0.7;
-                        double y1 = maxOutLumi * 0.15;
-                        double x2 = metadata.displayMaxLuminance * 0.9;
-                        double y2 = maxOutLumi * 0.45;
-                        double x3 = metadata.displayMaxLuminance;
-                        double y3 = maxOutLumi;
-
-                        double c1 = y1 / 3.0;
-                        double c2 = y2 / 2.0;
-                        double c3 = y3 / 1.5;
-
-                        targetNits = xyz.y;
-
-                        if (targetNits <= x0) {
-                            // scale [0.0, x0] to [0.0, y0] linearly
-                            double slope = y0 / x0;
-                            targetNits *= slope;
-                        } else if (targetNits <= x1) {
-                            // scale [x0, x1] to [y0, y1] using a curve
-                            double t = (targetNits - x0) / (x1 - x0);
-                            targetNits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 +
-                                    t * t * y1;
-                        } else if (targetNits <= x2) {
-                            // scale [x1, x2] to [y1, y2] using a curve
-                            double t = (targetNits - x1) / (x2 - x1);
-                            targetNits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 +
-                                    t * t * y2;
-                        } else {
-                            // scale [x2, x3] to [y2, y3] using a curve
-                            double t = (targetNits - x2) / (x3 - x2);
-                            targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 +
-                                    t * t * y3;
-                        }
-                    } break;
-                    default:
-                        // For completeness, this is tone-mapping from SDR to SDR, where this is
-                        // just a no-op.
-                        targetNits = xyz.y;
-                        break;
-                }
+                            if ((destinationDataspaceInt & kTransferMask) == kTransferHLG) {
+                                targetNits *= std::pow(targetNits / 1000.0, -0.2 / 1.2);
+                            }
+                        } break;
+                        default:
+                            // For completeness, this is tone-mapping from SDR to SDR, where this is
+                            // just a no-op.
+                            targetNits = xyz.y;
+                            break;
+                    }
+            }
+            gains.push_back(targetNits / xyz.y);
         }
-
-        return targetNits / xyz.y;
+        return gains;
     }
 };
 
@@ -404,6 +469,7 @@
         program.append(R"(
                 uniform float in_libtonemap_displayMaxLuminance;
                 uniform float in_libtonemap_inputMaxLuminance;
+                uniform float in_libtonemap_hlgGamma;
             )");
         switch (sourceDataspaceInt & kTransferMask) {
             case kTransferST2084:
@@ -421,14 +487,15 @@
                         // HLG output.
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(float maxRGB) {
-                                        return clamp(maxRGB, 0.0, 1000.0);
+                                        float nits = clamp(maxRGB, 0.0, 1000.0);
+                                        float gamma = (1 - in_libtonemap_hlgGamma)
+                                                / in_libtonemap_hlgGamma;
+                                        return nits * pow(nits / 1000.0, gamma);
                                     }
                                 )");
                         break;
 
                     default:
-                        // Here we're mapping from HDR to SDR content, so interpolate using a
-                        // Hermitian polynomial onto the smaller luminance range.
                         program.append(R"(
                                 float libtonemap_OETFTone(float channel) {
                                     channel = channel / 10000.0;
@@ -492,8 +559,15 @@
                 break;
             case kTransferHLG:
                 switch (destinationDataspaceInt & kTransferMask) {
-                    // HLG -> HDR does not tone-map at all
+                    // HLG uses the OOTF from BT 2100.
                     case kTransferST2084:
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(float maxRGB) {
+                                        return maxRGB
+                                                * pow(maxRGB / 1000.0, in_libtonemap_hlgGamma - 1);
+                                    }
+                                )");
+                        break;
                     case kTransferHLG:
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(float maxRGB) {
@@ -502,13 +576,14 @@
                                 )");
                         break;
                     default:
-                        // libshaders follows BT2100 OOTF, but with a nominal peak display luminance
-                        // of 1000 nits. Renormalize to max display luminance if we're tone-mapping
-                        // down to SDR, as libshaders normalizes all SDR output from [0,
-                        // maxDisplayLumins] -> [0, 1]
+                        // Follow BT 2100 and renormalize to max display luminance if we're
+                        // tone-mapping down to SDR, as libshaders normalizes all SDR output from
+                        // [0, maxDisplayLumins] -> [0, 1]
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(float maxRGB) {
-                                        return maxRGB * in_libtonemap_displayMaxLuminance / 1000.0;
+                                        return maxRGB
+                                                * pow(maxRGB / 1000.0, in_libtonemap_hlgGamma - 1)
+                                                * in_libtonemap_displayMaxLuminance / 1000.0;
                                     }
                                 )");
                         break;
@@ -540,103 +615,116 @@
         // Hardcode the max content luminance to a "reasonable" level
         static const constexpr float kContentMaxLuminance = 4000.f;
         std::vector<ShaderUniform> uniforms;
-        uniforms.reserve(2);
+        uniforms.reserve(3);
         uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
                             .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
         uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
                             .value = buildUniformValue<float>(kContentMaxLuminance)});
+        uniforms.push_back({.name = "in_libtonemap_hlgGamma",
+                            .value = buildUniformValue<float>(
+                                    computeHlgGamma(metadata.currentDisplayLuminance))});
         return uniforms;
     }
 
-    double lookupTonemapGain(
+    std::vector<Gain> lookupTonemapGain(
             aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
             aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
-            vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override {
-        double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b});
+            const std::vector<Color>& colors, const Metadata& metadata) override {
+        std::vector<Gain> gains;
+        gains.reserve(colors.size());
 
-        if (maxRGB <= 0.0) {
-            return 1.0;
-        }
+        // Precompute constants for HDR->SDR tonemapping parameters
+        constexpr double maxInLumi = 4000;
+        const double maxOutLumi = metadata.displayMaxLuminance;
 
-        const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
-        const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+        const double x1 = maxOutLumi * 0.65;
+        const double y1 = x1;
 
-        double targetNits = 0.0;
-        switch (sourceDataspaceInt & kTransferMask) {
-            case kTransferST2084:
-                switch (destinationDataspaceInt & kTransferMask) {
-                    case kTransferST2084:
-                        targetNits = maxRGB;
-                        break;
-                    case kTransferHLG:
-                        // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
-                        // we'll clamp the luminance range in case we're mapping from PQ input to
-                        // HLG output.
-                        targetNits = std::clamp(maxRGB, 0.0, 1000.0);
-                        break;
-                    default:
-                        // Here we're mapping from HDR to SDR content, so interpolate using a
-                        // Hermitian polynomial onto the smaller luminance range.
+        const double x3 = maxInLumi;
+        const double y3 = maxOutLumi;
 
-                        double maxInLumi = 4000;
-                        double maxOutLumi = metadata.displayMaxLuminance;
+        const double x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+        const double y2 = maxOutLumi * 0.9;
 
-                        targetNits = maxRGB;
+        const double greyNorm1 = OETF_ST2084(x1);
+        const double greyNorm2 = OETF_ST2084(x2);
+        const double greyNorm3 = OETF_ST2084(x3);
 
-                        double x1 = maxOutLumi * 0.65;
-                        double y1 = x1;
+        const double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+        const double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
 
-                        double x3 = maxInLumi;
-                        double y3 = maxOutLumi;
+        const double hlgGamma = computeHlgGamma(metadata.currentDisplayLuminance);
 
-                        double x2 = x1 + (x3 - x1) * 4.0 / 17.0;
-                        double y2 = maxOutLumi * 0.9;
+        for (const auto [linearRGB, _] : colors) {
+            double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b});
 
-                        const double greyNorm1 = OETF_ST2084(x1);
-                        const double greyNorm2 = OETF_ST2084(x2);
-                        const double greyNorm3 = OETF_ST2084(x3);
+            if (maxRGB <= 0.0) {
+                gains.push_back(1.0);
+                continue;
+            }
 
-                        double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
-                        double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
+            const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+            const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
 
-                        if (targetNits < x1) {
+            double targetNits = 0.0;
+            switch (sourceDataspaceInt & kTransferMask) {
+                case kTransferST2084:
+                    switch (destinationDataspaceInt & kTransferMask) {
+                        case kTransferST2084:
+                            targetNits = maxRGB;
                             break;
-                        }
-
-                        if (targetNits > maxInLumi) {
-                            targetNits = maxOutLumi;
+                        case kTransferHLG:
+                            // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG,
+                            // so we'll clamp the luminance range in case we're mapping from PQ
+                            // input to HLG output.
+                            targetNits = std::clamp(maxRGB, 0.0, 1000.0);
+                            targetNits *= pow(targetNits / 1000.0, (1 - hlgGamma) / (hlgGamma));
                             break;
-                        }
+                        default:
+                            targetNits = maxRGB;
+                            if (targetNits < x1) {
+                                break;
+                            }
 
-                        const double greyNits = OETF_ST2084(targetNits);
+                            if (targetNits > maxInLumi) {
+                                targetNits = maxOutLumi;
+                                break;
+                            }
 
-                        if (greyNits <= greyNorm2) {
-                            targetNits = (greyNits - greyNorm2) * slope2 + y2;
-                        } else if (greyNits <= greyNorm3) {
-                            targetNits = (greyNits - greyNorm3) * slope3 + y3;
-                        } else {
-                            targetNits = maxOutLumi;
-                        }
-                        break;
-                }
-                break;
-            case kTransferHLG:
-                switch (destinationDataspaceInt & kTransferMask) {
-                    case kTransferST2084:
-                    case kTransferHLG:
-                        targetNits = maxRGB;
-                        break;
-                    default:
-                        targetNits = maxRGB * metadata.displayMaxLuminance / 1000.0;
-                        break;
-                }
-                break;
-            default:
-                targetNits = maxRGB;
-                break;
+                            const double greyNits = OETF_ST2084(targetNits);
+
+                            if (greyNits <= greyNorm2) {
+                                targetNits = (greyNits - greyNorm2) * slope2 + y2;
+                            } else if (greyNits <= greyNorm3) {
+                                targetNits = (greyNits - greyNorm3) * slope3 + y3;
+                            } else {
+                                targetNits = maxOutLumi;
+                            }
+                            break;
+                    }
+                    break;
+                case kTransferHLG:
+                    switch (destinationDataspaceInt & kTransferMask) {
+                        case kTransferST2084:
+                            targetNits = maxRGB * pow(maxRGB / 1000.0, hlgGamma - 1);
+                            break;
+                        case kTransferHLG:
+                            targetNits = maxRGB;
+                            break;
+                        default:
+                            targetNits = maxRGB * pow(maxRGB / 1000.0, hlgGamma - 1) *
+                                    metadata.displayMaxLuminance / 1000.0;
+                            break;
+                    }
+                    break;
+                default:
+                    targetNits = maxRGB;
+                    break;
+            }
+
+            gains.push_back(targetNits / maxRGB);
         }
-
-        return targetNits / maxRGB;
+        return gains;
     }
 };
 
@@ -658,4 +746,4 @@
 
     return sToneMapper.get();
 }
-} // namespace android::tonemap
\ No newline at end of file
+} // namespace android::tonemap
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 73b63e3..3a4b6c5 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -334,60 +334,50 @@
 QueuedInputListener::QueuedInputListener(InputListenerInterface& innerListener)
       : mInnerListener(innerListener) {}
 
-QueuedInputListener::~QueuedInputListener() {
-    size_t count = mArgsQueue.size();
-    for (size_t i = 0; i < count; i++) {
-        delete mArgsQueue[i];
-    }
-}
-
 void QueuedInputListener::notifyConfigurationChanged(
         const NotifyConfigurationChangedArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifyConfigurationChangedArgs>(*args));
 }
 
 void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifyKeyArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifyKeyArgs>(*args));
 }
 
 void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifyMotionArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifyMotionArgs>(*args));
 }
 
 void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifySwitchArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifySwitchArgs>(*args));
 }
 
 void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifySensorArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifySensorArgs>(*args));
 }
 
 void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifyVibratorStateArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifyVibratorStateArgs>(*args));
 }
 
 void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifyDeviceResetArgs>(*args));
 }
 
 void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.push_back(new NotifyPointerCaptureChangedArgs(*args));
+    mArgsQueue.emplace_back(std::make_unique<NotifyPointerCaptureChangedArgs>(*args));
 }
 
 void QueuedInputListener::flush() {
-    size_t count = mArgsQueue.size();
-    for (size_t i = 0; i < count; i++) {
-        NotifyArgs* args = mArgsQueue[i];
+    for (const std::unique_ptr<NotifyArgs>& args : mArgsQueue) {
         args->notify(mInnerListener);
-        delete args;
     }
     mArgsQueue.clear();
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 06ad6a8..6bfac6c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1032,6 +1032,21 @@
                     }
                 }
             }
+
+            // If a new up event comes in, and the pending event with same key code has been asked
+            // to try again later because of the policy. We have to reset the intercept key wake up
+            // time for it may have been handled in the policy and could be dropped.
+            if (keyEntry.action == AKEY_EVENT_ACTION_UP && mPendingEvent &&
+                mPendingEvent->type == EventEntry::Type::KEY) {
+                KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);
+                if (pendingKey.keyCode == keyEntry.keyCode &&
+                    pendingKey.interceptKeyResult ==
+                            KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
+                    pendingKey.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+                    pendingKey.interceptKeyWakeupTime = 0;
+                    needWake = true;
+                }
+            }
             break;
         }
 
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index dff5894..d9822ce 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -274,7 +274,6 @@
 
 public:
     explicit QueuedInputListener(InputListenerInterface& innerListener);
-    virtual ~QueuedInputListener();
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
     virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -289,7 +288,7 @@
 
 private:
     InputListenerInterface& mInnerListener;
-    std::vector<NotifyArgs*> mArgsQueue;
+    std::vector<std::unique_ptr<NotifyArgs>> mArgsQueue;
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 9633932..838e6aa 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -289,6 +289,13 @@
         ASSERT_EQ(token, *receivedToken);
     }
 
+    /**
+     * Set policy timeout. A value of zero means next key will not be intercepted.
+     */
+    void setInterceptKeyTimeout(std::chrono::milliseconds timeout) {
+        mInterceptKeyTimeout = timeout;
+    }
+
 private:
     std::mutex mLock;
     std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -311,6 +318,8 @@
     sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
     bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
 
+    std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
+
     // All three ANR-related callbacks behave the same way, so we use this generic function to wait
     // for a specific container to become non-empty. When the container is non-empty, return the
     // first entry from the container and erase it.
@@ -429,12 +438,20 @@
         return true;
     }
 
-    void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+    void interceptKeyBeforeQueueing(const KeyEvent* inputEvent, uint32_t&) override {
+        if (inputEvent->getAction() == AKEY_EVENT_ACTION_UP) {
+            // Clear intercept state when we handled the event.
+            mInterceptKeyTimeout = 0ms;
+        }
+    }
 
     void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
 
     nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
-        return 0;
+        nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
+        // Clear intercept state so we could dispatch the event in next wake.
+        mInterceptKeyTimeout = 0ms;
+        return delay;
     }
 
     bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
@@ -2182,6 +2199,50 @@
                          0 /*expectedFlags*/);
 }
 
+TEST_F(InputDispatcherTest, InterceptKeyByPolicy) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFocusable(true);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
+    window->consumeFocusEvent(true);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    const std::chrono::milliseconds interceptKeyTimeout = 50ms;
+    const nsecs_t injectTime = keyArgs.eventTime;
+    mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout);
+    mDispatcher->notifyKey(&keyArgs);
+    // The dispatching time should be always greater than or equal to intercept key timeout.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    ASSERT_TRUE((systemTime(SYSTEM_TIME_MONOTONIC) - injectTime) >=
+                std::chrono::nanoseconds(interceptKeyTimeout).count());
+}
+
+TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFocusable(true);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+
+    window->consumeFocusEvent(true);
+
+    NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+    mFakePolicy->setInterceptKeyTimeout(150ms);
+    mDispatcher->notifyKey(&keyDown);
+    mDispatcher->notifyKey(&keyUp);
+
+    // Window should receive key event immediately when same key up.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+}
+
 /**
  * Ensure the correct coordinate spaces are used by InputDispatcher.
  *
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index aef55d4..4e67a63 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -684,8 +684,10 @@
             visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent()));
     outputLayerState.shadowRegion = shadowRegion;
     outputLayerState.outputSpaceBlockingRegionHint =
-            layerFEState->compositionType == Composition::DISPLAY_DECORATION ? transparentRegion
-                                                                             : Region();
+            layerFEState->compositionType == Composition::DISPLAY_DECORATION
+            ? outputState.transform.transform(
+                      transparentRegion.intersect(outputState.layerStackSpace.getContent()))
+            : Region();
 }
 
 void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 6749427..da1f7e4 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -61,6 +61,9 @@
     dumpVal(out, "shadowRegion", shadowRegion);
 
     out.append("      ");
+    dumpVal(out, "outputSpaceBlockingRegionHint", outputSpaceBlockingRegionHint);
+
+    out.append("      ");
     dumpVal(out, "forceClientComposition", forceClientComposition);
     dumpVal(out, "clearClientTarget", clearClientTarget);
     dumpVal(out, "displayFrame", displayFrame);
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index a8cad53..af013b0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -134,7 +134,9 @@
                           std::optional<aidl::android::hardware::graphics::common::
                                                 DisplayDecorationSupport>* support));
     MOCK_METHOD2(setIdleTimerEnabled, status_t(PhysicalDisplayId, std::chrono::milliseconds));
-    MOCK_METHOD1(hasDisplayIdleTimerCapability, bool(PhysicalDisplayId displayId));
+    MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
+                (const, override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 54eb8f8..66a9ef7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1310,6 +1310,7 @@
     static const Region kLowerHalfBoundsNoRotation;
     static const Region kFullBounds90Rotation;
     static const Region kTransparentRegionHint;
+    static const Region kTransparentRegionHint90Rotation;
 
     StrictMock<OutputPartialMock> mOutput;
     LayerFESet mGeomSnapshots;
@@ -1328,7 +1329,9 @@
 const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
         Region(Rect(0, 0, 200, 100));
 const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHint =
-        Region(Rect(0, 0, 100, 100));
+        Region(Rect(25, 20, 50, 75));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHint90Rotation =
+        Region(Rect(125, 25, 180, 50));
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
     EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
@@ -1779,6 +1782,24 @@
     EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint, RegionEq(Region()));
 }
 
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, blockingRegionIsInOutputSpace) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.compositionType =
+            aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+
+    mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
+    mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint,
+                RegionEq(kTransparentRegionHint90Rotation));
+}
+
 /*
  * Output::present()
  */
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 92592f7..8b376f1 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -249,6 +249,7 @@
         case OptionalFeature::DisplayBrightnessCommand:
         case OptionalFeature::BootDisplayConfig:
         case OptionalFeature::KernelIdleTimer:
+        case OptionalFeature::PhysicalDisplayOrientation:
             return true;
     }
 }
@@ -1126,5 +1127,17 @@
     return Error::NONE;
 }
 
+Error AidlComposer::getPhysicalDisplayOrientation(Display displayId,
+                                                  AidlTransform* outDisplayOrientation) {
+    const auto status =
+            mAidlComposerClient->getDisplayPhysicalOrientation(translate<int64_t>(displayId),
+                                                               outDisplayOrientation);
+    if (!status.isOk()) {
+        ALOGE("getPhysicalDisplayOrientation failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
 } // namespace Hwc2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 6c0f636..28ff167 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -223,6 +223,9 @@
                                       std::optional<DisplayDecorationSupport>* support) override;
     Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) override;
 
+    Error getPhysicalDisplayOrientation(Display displayId,
+                                        AidlTransform* outDisplayOrientation) override;
+
 private:
     // Many public functions above simply write a command into the command
     // queue to batch the calls.  validateDisplay and presentDisplay will call
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 6abe7d1..7d9946d 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -38,6 +38,7 @@
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 
+#include <aidl/android/hardware/graphics/common/Transform.h>
 #include <optional>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -80,6 +81,7 @@
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
+using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
 
 class Composer {
 public:
@@ -94,6 +96,7 @@
         DisplayBrightnessCommand,
         BootDisplayConfig,
         KernelIdleTimer,
+        PhysicalDisplayOrientation,
     };
 
     virtual bool isSupported(OptionalFeature) const = 0;
@@ -277,6 +280,8 @@
             std::optional<::aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) = 0;
     virtual Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) = 0;
+    virtual Error getPhysicalDisplayOrientation(Display displayId,
+                                                AidlTransform* outDisplayOrientation) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 6501276..c0432bf 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -154,6 +154,11 @@
             isCapabilitySupported;
 }
 
+Error Display::getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const {
+    auto error = mComposer.getPhysicalDisplayOrientation(mId, outTransform);
+    return static_cast<Error>(error);
+}
+
 Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
     std::vector<Hwc2::Layer> layerIds;
     std::vector<Composition> types;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index c03cede..d78562d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -169,6 +169,8 @@
                     support) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setIdleTimerEnabled(
             std::chrono::milliseconds timeout) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getPhysicalDisplayOrientation(
+            Hwc2::AidlTransform* outTransform) const = 0;
 };
 
 namespace impl {
@@ -256,6 +258,7 @@
     bool isVsyncPeriodSwitchSupported() const override;
     bool hasDisplayIdleTimerCapability() const override;
     void onLayerDestroyed(hal::HWLayerId layerId) override;
+    hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override;
 
 private:
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7b9ffed..e2e4a99 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -987,9 +987,19 @@
     return NO_ERROR;
 }
 
-bool HWComposer::hasDisplayIdleTimerCapability(PhysicalDisplayId displayId) {
+bool HWComposer::hasDisplayIdleTimerCapability(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return mDisplayData[displayId].hwcDisplay->hasDisplayIdleTimerCapability();
+    return mDisplayData.at(displayId).hwcDisplay->hasDisplayIdleTimerCapability();
+}
+
+Hwc2::AidlTransform HWComposer::getPhysicalDisplayOrientation(PhysicalDisplayId displayId) const {
+    ATRACE_CALL();
+    RETURN_IF_INVALID_DISPLAY(displayId, Hwc2::AidlTransform::NONE);
+    Hwc2::AidlTransform outTransform;
+    const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
+    const auto error = hwcDisplay->getPhysicalDisplayOrientation(&outTransform);
+    RETURN_IF_HWC_ERROR(error, displayId, Hwc2::AidlTransform::NONE);
+    return outTransform;
 }
 
 void HWComposer::loadLayerMetadataSupport() {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 3dd910b..91fe1b7 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -276,7 +276,8 @@
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) = 0;
     virtual status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) = 0;
-    virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) = 0;
+    virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const = 0;
+    virtual Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const = 0;
 };
 
 namespace impl {
@@ -414,7 +415,8 @@
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) override;
     status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) override;
-    bool hasDisplayIdleTimerCapability(PhysicalDisplayId) override;
+    bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const override;
+    Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 33adceb..e8dc084 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -237,6 +237,7 @@
         case OptionalFeature::DisplayBrightnessCommand:
         case OptionalFeature::BootDisplayConfig:
         case OptionalFeature::KernelIdleTimer:
+        case OptionalFeature::PhysicalDisplayOrientation:
             return false;
     }
 }
@@ -1331,6 +1332,11 @@
                      "OptionalFeature::KernelIdleTimer is not supported on HIDL");
 }
 
+Error HidlComposer::getPhysicalDisplayOrientation(Display, AidlTransform*) {
+    LOG_ALWAYS_FATAL("getPhysicalDisplayOrientation should have never been called on this as "
+                     "OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL");
+}
+
 void HidlComposer::registerCallback(ComposerCallback& callback) {
     const bool vsyncSwitchingSupported =
             isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index a1ea4f2..5869ae5 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -334,6 +334,9 @@
                     support) override;
     Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) override;
 
+    Error getPhysicalDisplayOrientation(Display displayId,
+                                        AidlTransform* outDisplayOrientation) override;
+
 private:
     class CommandWriter : public CommandWriterBase {
     public:
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index ec59888..66beff2 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -669,12 +669,12 @@
             // frame in the trace.
             nsecs_t endTime =
                     (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
-            packet->set_timestamp(static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta +
-                                                        monoBootOffset));
+            const auto timestamp = endTime - kPredictionExpiredStartTimeDelta;
+            packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
         } else {
-            packet->set_timestamp(static_cast<uint64_t>(monoBootOffset + mActuals.startTime == 0
-                                                                ? mPredictions.startTime
-                                                                : mActuals.startTime));
+            const auto timestamp =
+                    mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime;
+            packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
         }
 
         auto* event = packet->set_frame_timeline_event();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 0fb16f2..48a9bc5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2007 The Android Open Source Project
  *
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index a1e1455..896f254 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -26,18 +26,12 @@
 namespace android {
 namespace {
 
-struct ReparentForDrawing {
-    const sp<Layer>& oldParent;
-
-    ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
-                       const Rect& drawingBounds)
-          : oldParent(oldParent) {
+void reparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+                   const Rect& drawingBounds) {
         // Compute and cache the bounds for the new parent layer.
         newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
-                                 0.f /* shadowRadius */);
+            0.f /* shadowRadius */);
         oldParent->setChildrenDrawingParent(newParent);
-    }
-    ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
 };
 
 } // namespace
@@ -114,11 +108,19 @@
     } else {
         // In the "childrenOnly" case we reparent the children to a screenshot
         // layer which has no properties set and which does not draw.
+        //  We hold the statelock as the reparent-for-drawing operation modifies the
+        //  hierarchy and there could be readers on Binder threads, like dump.
         sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
-                {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()});
-
-        ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
+                  {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()});
+        {
+            Mutex::Autolock _l(mFlinger.mStateLock);
+            reparentForDrawing(mLayer, screenshotParentLayer, sourceCrop);
+        }
         drawLayers();
+        {
+            Mutex::Autolock _l(mFlinger.mStateLock);
+            mLayer->setChildrenDrawingParent(mLayer);
+        }
     }
 }
 
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
index 6a90694..41273e0 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -46,6 +46,7 @@
     Rect getSourceCrop() const override;
 
     void render(std::function<void()> drawLayers) override;
+    virtual sp<Layer> getParentLayer() const { return mLayer; }
 
 private:
     const sp<Layer> mLayer;
@@ -58,4 +59,4 @@
     const bool mChildrenOnly;
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index c9f7f46..387364c 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -4,6 +4,7 @@
 #include <ui/Transform.h>
 
 #include <functional>
+#include "Layer.h"
 
 namespace android {
 
@@ -85,6 +86,10 @@
     // Returns the source display viewport.
     const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; }
 
+    // If this is a LayerRenderArea, return the root layer of the
+    // capture operation.
+    virtual sp<Layer> getParentLayer() const { return nullptr; }
+
 protected:
     const bool mAllowSecureLayers;
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0ee3121..2edc05b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -324,7 +324,6 @@
 uint32_t SurfaceFlinger::maxGraphicsWidth;
 uint32_t SurfaceFlinger::maxGraphicsHeight;
 bool SurfaceFlinger::hasWideColorDisplay;
-ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
 bool SurfaceFlinger::useContextPriority;
 Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
@@ -416,22 +415,6 @@
 
     useContextPriority = use_context_priority(true);
 
-    using Values = SurfaceFlingerProperties::primary_display_orientation_values;
-    switch (primary_display_orientation(Values::ORIENTATION_0)) {
-        case Values::ORIENTATION_0:
-            break;
-        case Values::ORIENTATION_90:
-            internalDisplayOrientation = ui::ROTATION_90;
-            break;
-        case Values::ORIENTATION_180:
-            internalDisplayOrientation = ui::ROTATION_180;
-            break;
-        case Values::ORIENTATION_270:
-            internalDisplayOrientation = ui::ROTATION_270;
-            break;
-    }
-    ALOGV("Internal Display Orientation: %s", toCString(internalDisplayOrientation));
-
     mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries();
 
     // debugging stuff...
@@ -1014,9 +997,7 @@
 
     info->secure = display->isSecure();
     info->deviceProductInfo = display->getDeviceProductInfo();
-
-    // TODO: Scale this to multiple displays.
-    info->installOrientation = display->isPrimary() ? internalDisplayOrientation : ui::ROTATION_0;
+    info->installOrientation = display->getPhysicalOrientation();
 
     return NO_ERROR;
 }
@@ -1052,9 +1033,8 @@
         auto [width, height] = mode->getResolution();
         auto [xDpi, yDpi] = mode->getDpi();
 
-        if (display->isPrimary() &&
-            (internalDisplayOrientation == ui::ROTATION_90 ||
-             internalDisplayOrientation == ui::ROTATION_270)) {
+        if (const auto physicalOrientation = display->getPhysicalOrientation();
+            physicalOrientation == ui::ROTATION_90 || physicalOrientation == ui::ROTATION_270) {
             std::swap(width, height);
             std::swap(xDpi, yDpi);
         }
@@ -2406,6 +2386,42 @@
     return true;
 }
 
+ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
+                                                           bool isPrimary) const {
+    const auto id = PhysicalDisplayId::tryCast(displayId);
+    if (!id) {
+        return ui::ROTATION_0;
+    }
+    if (getHwComposer().getComposer()->isSupported(
+                Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
+        switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
+            case Hwc2::AidlTransform::ROT_90:
+                return ui::ROTATION_90;
+            case Hwc2::AidlTransform::ROT_180:
+                return ui::ROTATION_180;
+            case Hwc2::AidlTransform::ROT_270:
+                return ui::ROTATION_270;
+            default:
+                return ui::ROTATION_0;
+        }
+    }
+
+    if (isPrimary) {
+        using Values = SurfaceFlingerProperties::primary_display_orientation_values;
+        switch (primary_display_orientation(Values::ORIENTATION_0)) {
+            case Values::ORIENTATION_90:
+                return ui::ROTATION_90;
+            case Values::ORIENTATION_180:
+                return ui::ROTATION_180;
+            case Values::ORIENTATION_270:
+                return ui::ROTATION_270;
+            default:
+                break;
+        }
+    }
+    return ui::ROTATION_0;
+}
+
 void SurfaceFlinger::postComposition() {
     ATRACE_CALL();
     ALOGV("postComposition");
@@ -2880,7 +2896,8 @@
     }
 
     creationArgs.physicalOrientation =
-            creationArgs.isPrimary ? internalDisplayOrientation : ui::ROTATION_0;
+            getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary);
+    ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
 
     // virtual displays are always considered enabled
     creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
@@ -5442,7 +5459,9 @@
     /*
      * Dump flag/property manager state
      */
-    mFlagManager->dump(result);
+    if (mFlagManager != nullptr) {
+        mFlagManager->dump(result);
+    }
 
     result.append(mTimeStats->miniDump());
     result.append("\n");
@@ -6505,19 +6524,6 @@
         // and failed if display is not in native mode. This provide a way to force using native
         // colors when capture.
         dataspace = args.dataspace;
-        if (dataspace == ui::Dataspace::UNKNOWN) {
-            auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
-                return display.getLayerStack() == layerStack;
-            });
-            if (!display) {
-                // If the layer is not on a display, use the dataspace for the default display.
-                display = getDefaultDisplayDeviceLocked();
-            }
-
-            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
-            dataspace = pickDataspaceFromColorMode(colorMode);
-        }
-
     } // mStateLock
 
     // really small crop or frameScale
@@ -6646,7 +6652,7 @@
 
         renderArea->render([&] {
             renderEngineResultFuture =
-                    renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+                    renderScreenImpl(*renderArea, traverseLayers, buffer,
                                            canCaptureBlackoutContent, regionSampling, grayscale,
                                            captureResults);
         });
@@ -6679,7 +6685,7 @@
     }
 }
 
-std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScreenImplLocked(
+std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScreenImpl(
         const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer,
         bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
@@ -6703,7 +6709,22 @@
     }
 
     captureResults.buffer = buffer->getBuffer();
-    captureResults.capturedDataspace = renderArea.getReqDataSpace();
+    auto dataspace = renderArea.getReqDataSpace();
+    auto parent = renderArea.getParentLayer();
+    if ((dataspace == ui::Dataspace::UNKNOWN) && (parent != nullptr)) {
+        Mutex::Autolock lock(mStateLock);
+        auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+            return display.getLayerStack() == layerStack;
+        });
+        if (!display) {
+            // If the layer is not on a display, use the dataspace for the default display.
+            display = getDefaultDisplayDeviceLocked();
+        }
+
+        const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+        dataspace = pickDataspaceFromColorMode(colorMode);
+    }
+    captureResults.capturedDataspace = dataspace;
 
     const auto reqWidth = renderArea.getReqWidth();
     const auto reqHeight = renderArea.getReqHeight();
@@ -6721,7 +6742,7 @@
     clientCompositionDisplay.clip = sourceCrop;
     clientCompositionDisplay.orientation = rotation;
 
-    clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
+    clientCompositionDisplay.outputDataspace = dataspace;
     clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
 
     const float colorSaturation = grayscale ? 0 : 1;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 01732be..df59d50 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -256,8 +256,6 @@
     // found on devices with wide color gamut (e.g. Display-P3) display.
     static bool hasWideColorDisplay;
 
-    static ui::Rotation internalDisplayOrientation;
-
     // Indicate if device wants color management on its display.
     static const constexpr bool useColorManagement = true;
 
@@ -375,6 +373,7 @@
     friend class MonitoredProducer;
     friend class RefreshRateOverlay;
     friend class RegionSamplingThread;
+    friend class LayerRenderArea;
     friend class LayerTracing;
 
     // For unit tests
@@ -863,10 +862,10 @@
             RenderAreaFuture, TraverseLayersFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, const sp<IScreenCaptureListener>&);
-    std::shared_future<renderengine::RenderEngineResult> renderScreenImplLocked(
+    std::shared_future<renderengine::RenderEngineResult> renderScreenImpl(
             const RenderArea&, TraverseLayersFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
-            bool regionSampling, bool grayscale, ScreenCaptureResults&);
+            bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock);
 
     // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
     // matching ownerUid
@@ -1146,6 +1145,9 @@
 
     bool isHdrLayer(Layer* layer) const;
 
+    ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
+            REQUIRES(mStateLock);
+
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
     pid_t mPid;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index afc1abd..a5a716d 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -29,9 +29,6 @@
         LatchUnsignaledConfig::Disabled,
 };
 
-static constexpr ui::Rotation kRotations[] = {ui::Rotation::Rotation0, ui::Rotation::Rotation90,
-                                              ui::Rotation::Rotation180, ui::Rotation::Rotation270};
-
 static constexpr BnSurfaceComposer::ISurfaceComposerTag kSurfaceComposerTags[]{
         BnSurfaceComposer::BOOT_FINISHED,
         BnSurfaceComposer::CREATE_CONNECTION,
@@ -134,7 +131,6 @@
     mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>();
     mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>();
     mFlinger->hasWideColorDisplay = mFdp.ConsumeBool();
-    mFlinger->internalDisplayOrientation = mFdp.PickValueInArray(kRotations);
     mFlinger->useContextPriority = mFdp.ConsumeBool();
 
     mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces);
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 1669075..15c9d19 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -244,8 +244,8 @@
                                                                       HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                                                       usage);
 
-    auto result = mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
-                                                  forSystem, regionSampling);
+    auto result = mFlinger.renderScreenImpl(*renderArea, traverseLayers, mCaptureScreenBuffer,
+                                            forSystem, regionSampling);
     EXPECT_TRUE(result.valid());
 
     auto& [status, drawFence] = result.get();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index aff4d83..bf2465f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -394,12 +394,12 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto renderScreenImplLocked(const RenderArea& renderArea,
+    auto renderScreenImpl(const RenderArea& renderArea,
                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,
                                 const std::shared_ptr<renderengine::ExternalTexture>& buffer,
                                 bool forSystem, bool regionSampling) {
         ScreenCaptureResults captureResults;
-        return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem,
+        return mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer, forSystem,
                                                 regionSampling, false /* grayscale */,
                                                 captureResults);
     }
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 996f835..c1d41bb 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -160,6 +160,7 @@
                  Error(Display, std::optional<DisplayDecorationSupport>*));
     MOCK_METHOD2(setIdleTimerEnabled, Error(Display, std::chrono::milliseconds));
     MOCK_METHOD2(hasDisplayIdleTimerCapability, Error(Display, bool*));
+    MOCK_METHOD2(getPhysicalDisplayOrientation, Error(Display, AidlTransform*));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index d9faa06..ac2ab199c 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -101,6 +101,8 @@
             (override));
     MOCK_METHOD(hal::Error, setIdleTimerEnabled, (std::chrono::milliseconds), (override));
     MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (), (const override));
+    MOCK_METHOD(hal::Error, getPhysicalDisplayOrientation, (Hwc2::AidlTransform *),
+                (const override));
 };
 
 class Layer : public HWC2::Layer {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 8cb1b21..7664518 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -632,6 +632,7 @@
         switch (ext_bit) {
             case ProcHook::KHR_android_surface:
             case ProcHook::KHR_surface:
+            case ProcHook::KHR_surface_protected_capabilities:
             case ProcHook::EXT_swapchain_colorspace:
             case ProcHook::KHR_get_surface_capabilities2:
             case ProcHook::GOOGLE_surfaceless_query:
@@ -711,6 +712,7 @@
             case ProcHook::KHR_external_fence_capabilities:
             case ProcHook::KHR_get_surface_capabilities2:
             case ProcHook::KHR_surface:
+            case ProcHook::KHR_surface_protected_capabilities:
             case ProcHook::EXT_debug_report:
             case ProcHook::EXT_swapchain_colorspace:
             case ProcHook::GOOGLE_surfaceless_query:
@@ -924,15 +926,18 @@
     std::vector<VkExtensionProperties> loader_extensions;
     loader_extensions.push_back(
         {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION});
+    loader_extensions.push_back(
+        {VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME,
+         VK_KHR_SURFACE_PROTECTED_CAPABILITIES_SPEC_VERSION});
     loader_extensions.push_back({
         VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
         VK_KHR_ANDROID_SURFACE_SPEC_VERSION});
     loader_extensions.push_back({
         VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
         VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION});
-    loader_extensions.push_back({
-        VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
-        VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION});
+    loader_extensions.push_back(
+        {VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
+         VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION});
     loader_extensions.push_back({VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME,
                                  VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION});
 
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index f84fc88..b436db1 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -571,6 +571,7 @@
     if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
     if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image;
     if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
+    if (strcmp(name, "VK_KHR_surface_protected_capabilities") == 0) return ProcHook::KHR_surface_protected_capabilities;
     if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
     if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
     if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 6a6c5b3..079f9cc 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -47,6 +47,7 @@
         KHR_incremental_present,
         KHR_shared_presentable_image,
         KHR_surface,
+        KHR_surface_protected_capabilities,
         KHR_swapchain,
         ANDROID_external_memory_android_hardware_buffer,
         KHR_bind_memory2,
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 20a0aad..45bc4c9 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -719,7 +719,7 @@
 
     bool wide_color_support = false;
     uint64_t consumer_usage = 0;
-    bool swapchain_ext =
+    bool colorspace_ext =
         instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
     if (surface_handle == VK_NULL_HANDLE) {
         ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query;
@@ -748,7 +748,7 @@
 
         consumer_usage = surface.consumer_usage;
     }
-    wide_color_support = wide_color_support && swapchain_ext;
+    wide_color_support = wide_color_support && colorspace_ext;
 
     AHardwareBuffer_Desc desc = {};
     desc.width = 1;
@@ -762,7 +762,7 @@
         {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
         {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
 
-    if (swapchain_ext) {
+    if (colorspace_ext) {
         all_formats.emplace_back(VkSurfaceFormatKHR{
             VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
     }
@@ -865,6 +865,12 @@
                         .supportedUsageFlags;
             } break;
 
+            case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
+                VkSurfaceProtectedCapabilitiesKHR* protected_caps =
+                    reinterpret_cast<VkSurfaceProtectedCapabilitiesKHR*>(caps);
+                protected_caps->supportsProtected = VK_TRUE;
+            } break;
+
             default:
                 // Ignore all other extension structs
                 break;
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index cd25dd8..af56764 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -33,6 +33,7 @@
     'VK_KHR_incremental_present',
     'VK_KHR_shared_presentable_image',
     'VK_KHR_surface',
+    'VK_KHR_surface_protected_capabilities',
     'VK_KHR_swapchain',
 ]