[automerger skipped] Merge "Do not handle VSync event during resolution switch" am: 03e4e9d9f5 am: 661fcfbcae am: 89d1093444 am: 747e5f771c -s ours

am skip reason: Merged-In Ide53b0e5cff3b569593a2ed388d0ea66e0002e9d with SHA-1 087d37481e is already in history

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1918376

Change-Id: I25a5fa413b2964e79a67be54dd7537d7cadccb3b
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 7ef26bf..6d837c2 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -28,6 +28,7 @@
                services/vibratorservice/
                services/vr/
                vulkan/
+bpfmt = -d
 
 [Hook Scripts]
 owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6459443..bb1d206 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -244,6 +244,7 @@
         { OPT,      "events/kmem/ion_heap_shrink/enable" },
         { OPT,      "events/ion/ion_stat/enable" },
         { OPT,      "events/gpu_mem/gpu_mem_total/enable" },
+        { OPT,      "events/fastrpc/fastrpc_dma_stat/enable" },
     } },
     { "thermal",  "Thermal event", 0, {
         { REQ,      "events/thermal/thermal_temperature/enable" },
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index c3256fc..2207405 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,7 +77,7 @@
 #define LOG_TAG "installd"
 #endif
 
-// #define GRANULAR_LOCKS
+#define GRANULAR_LOCKS
 
 using android::base::ParseUint;
 using android::base::StringPrintf;
@@ -1590,7 +1590,7 @@
 }
 
 binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid,
-        int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
+        int64_t targetFreeBytes, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
 #ifndef GRANULAR_LOCKS
@@ -1601,6 +1601,7 @@
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     auto data_path = create_data_path(uuid_);
     auto noop = (flags & FLAG_FREE_CACHE_NOOP);
+    auto defy_target = (flags & FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES);
 
     int64_t free = data_disk_free(data_path);
     if (free < 0) {
@@ -1609,11 +1610,13 @@
 
     int64_t cleared = 0;
     int64_t needed = targetFreeBytes - free;
-    LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
-            << targetFreeBytes << "; needed " << needed;
+    if (!defy_target) {
+        LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
+                << targetFreeBytes << "; needed " << needed;
 
-    if (free >= targetFreeBytes) {
-        return ok();
+        if (free >= targetFreeBytes) {
+            return ok();
+        }
     }
 
     if (flags & FLAG_FREE_CACHE_V2) {
@@ -1706,12 +1709,6 @@
                 break;
             }
 
-            // Only keep clearing when we haven't pushed into reserved area
-            if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) {
-                LOG(DEBUG) << "Refusing to clear cached data in reserved space";
-                break;
-            }
-
             // Find the best tracker to work with; this might involve swapping
             // if the active tracker is no longer the most over quota
             bool nextBetter = active && !queue.empty()
@@ -1744,15 +1741,17 @@
                 cleared += item->size;
             }
 
-            // Verify that we're actually done before bailing, since sneaky
-            // apps might be using hardlinks
-            if (needed <= 0) {
-                free = data_disk_free(data_path);
-                needed = targetFreeBytes - free;
+            if (!defy_target) {
+                // Verify that we're actually done before bailing, since sneaky
+                // apps might be using hardlinks
                 if (needed <= 0) {
-                    break;
-                } else {
-                    LOG(WARNING) << "Expected to be done but still need " << needed;
+                    free = data_disk_free(data_path);
+                    needed = targetFreeBytes - free;
+                    if (needed <= 0) {
+                        break;
+                    } else {
+                        LOG(WARNING) << "Expected to be done but still need " << needed;
+                    }
                 }
             }
         }
@@ -1762,12 +1761,16 @@
         return error("Legacy cache logic no longer supported");
     }
 
-    free = data_disk_free(data_path);
-    if (free >= targetFreeBytes) {
-        return ok();
+    if (!defy_target) {
+        free = data_disk_free(data_path);
+        if (free >= targetFreeBytes) {
+            return ok();
+        } else {
+            return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
+                    targetFreeBytes, data_path.c_str(), free));
+        }
     } else {
-        return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
-                targetFreeBytes, data_path.c_str(), free));
+        return ok();
     }
 }
 
@@ -2662,22 +2665,20 @@
         const char* default_value = nullptr) {
     return data ? data->c_str() : default_value;
 }
-binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
-        const std::optional<std::string>& packageName, const std::string& instructionSet,
-        int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+binder::Status InstalldNativeService::dexopt(
+        const std::string& apkPath, int32_t uid, const std::string& packageName,
+        const std::string& instructionSet, int32_t dexoptNeeded,
+        const std::optional<std::string>& outputPath, int32_t dexFlags,
         const std::string& compilerFilter, const std::optional<std::string>& uuid,
         const std::optional<std::string>& classLoaderContext,
         const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
         const std::optional<std::string>& profileName,
         const std::optional<std::string>& dexMetadataPath,
-        const std::optional<std::string>& compilationReason,
-        bool* aidl_return) {
+        const std::optional<std::string>& compilationReason, bool* aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PATH(apkPath);
-    if (packageName && *packageName != "*") {
-        CHECK_ARGUMENT_PACKAGE_NAME(*packageName);
-    }
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(outputPath);
     CHECK_ARGUMENT_PATH(dexMetadataPath);
     const auto userId = multiuser_get_user_id(uid);
@@ -2685,13 +2686,13 @@
 
     const char* oat_dir = getCStr(outputPath);
     const char* instruction_set = instructionSet.c_str();
-    if (oat_dir != nullptr && !createOatDir(oat_dir, instruction_set).isOk()) {
+    if (oat_dir != nullptr && !createOatDir(packageName, oat_dir, instruction_set).isOk()) {
         // Can't create oat dir - let dexopt use cache dir.
         oat_dir = nullptr;
     }
 
     const char* apk_path = apkPath.c_str();
-    const char* pkgname = getCStr(packageName, "*");
+    const char* pkgname = packageName.c_str();
     const char* compiler_filter = compilerFilter.c_str();
     const char* volume_uuid = getCStr(uuid);
     const char* class_loader_context = getCStr(classLoaderContext);
@@ -2858,9 +2859,11 @@
     return res;
 }
 
-binder::Status InstalldNativeService::createOatDir(const std::string& oatDir,
-        const std::string& instructionSet) {
+binder::Status InstalldNativeService::createOatDir(const std::string& packageName,
+                                                   const std::string& oatDir,
+                                                   const std::string& instructionSet) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(oatDir);
     LOCK_PACKAGE();
 
@@ -2884,7 +2887,8 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
+binder::Status InstalldNativeService::rmPackageDir(const std::string& packageName,
+                                                   const std::string& packageDir) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(packageDir);
     LOCK_PACKAGE();
@@ -2898,9 +2902,12 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::linkFile(const std::string& relativePath,
-        const std::string& fromBase, const std::string& toBase) {
+binder::Status InstalldNativeService::linkFile(const std::string& packageName,
+                                               const std::string& relativePath,
+                                               const std::string& fromBase,
+                                               const std::string& toBase) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(fromBase);
     CHECK_ARGUMENT_PATH(toBase);
     LOCK_PACKAGE();
@@ -2928,9 +2935,12 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::moveAb(const std::string& apkPath,
-        const std::string& instructionSet, const std::string& outputPath) {
+binder::Status InstalldNativeService::moveAb(const std::string& packageName,
+                                             const std::string& apkPath,
+                                             const std::string& instructionSet,
+                                             const std::string& outputPath) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(apkPath);
     CHECK_ARGUMENT_PATH(outputPath);
     LOCK_PACKAGE();
@@ -2943,10 +2953,13 @@
     return success ? ok() : error();
 }
 
-binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
-        const std::string& instructionSet, const std::optional<std::string>& outputPath,
-        int64_t* _aidl_return) {
+binder::Status InstalldNativeService::deleteOdex(const std::string& packageName,
+                                                 const std::string& apkPath,
+                                                 const std::string& instructionSet,
+                                                 const std::optional<std::string>& outputPath,
+                                                 int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(apkPath);
     CHECK_ARGUMENT_PATH(outputPath);
     LOCK_PACKAGE();
@@ -2978,9 +2991,12 @@
 
 #endif
 
-binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,
-        android::base::unique_fd verityInputAshmem, int32_t contentSize) {
+binder::Status InstalldNativeService::installApkVerity(const std::string& packageName,
+                                                       const std::string& filePath,
+                                                       android::base::unique_fd verityInputAshmem,
+                                                       int32_t contentSize) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(filePath);
     LOCK_PACKAGE();
 
@@ -3060,9 +3076,11 @@
     return ok();
 }
 
-binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath,
+binder::Status InstalldNativeService::assertFsverityRootHashMatches(
+        const std::string& packageName, const std::string& filePath,
         const std::vector<uint8_t>& expectedHash) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(filePath);
     LOCK_PACKAGE();
 
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 09581bb..04662ea 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -119,16 +119,15 @@
             int32_t appId, const std::string& seInfo,
             int32_t targetSdkVersion, const std::string& fromCodePath);
 
-    binder::Status dexopt(const std::string& apkPath, int32_t uid,
-            const std::optional<std::string>& packageName, const std::string& instructionSet,
-            int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
-            const std::string& compilerFilter, const std::optional<std::string>& uuid,
-            const std::optional<std::string>& classLoaderContext,
-            const std::optional<std::string>& seInfo, bool downgrade,
-            int32_t targetSdkVersion, const std::optional<std::string>& profileName,
-            const std::optional<std::string>& dexMetadataPath,
-            const std::optional<std::string>& compilationReason,
-            bool* aidl_return);
+    binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::string& packageName,
+                          const std::string& instructionSet, int32_t dexoptNeeded,
+                          const std::optional<std::string>& outputPath, int32_t dexFlags,
+                          const std::string& compilerFilter, const std::optional<std::string>& uuid,
+                          const std::optional<std::string>& classLoaderContext,
+                          const std::optional<std::string>& seInfo, bool downgrade,
+                          int32_t targetSdkVersion, const std::optional<std::string>& profileName,
+                          const std::optional<std::string>& dexMetadataPath,
+                          const std::optional<std::string>& compilationReason, bool* aidl_return);
 
     binder::Status controlDexOptBlocking(bool block);
 
@@ -152,22 +151,25 @@
     binder::Status destroyProfileSnapshot(const std::string& packageName,
             const std::string& profileName);
 
-    binder::Status rmPackageDir(const std::string& packageDir);
+    binder::Status rmPackageDir(const std::string& packageName, const std::string& packageDir);
     binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes,
-            int64_t cacheReservedBytes, int32_t flags);
+            int32_t flags);
     binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid,
             const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
-    binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
-    binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
-            const std::string& toBase);
-    binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
-            const std::string& outputPath);
-    binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
-            const std::optional<std::string>& outputPath, int64_t* _aidl_return);
-    binder::Status installApkVerity(const std::string& filePath,
-            android::base::unique_fd verityInput, int32_t contentSize);
-    binder::Status assertFsverityRootHashMatches(const std::string& filePath,
-            const std::vector<uint8_t>& expectedHash);
+    binder::Status createOatDir(const std::string& packageName, const std::string& oatDir,
+                                const std::string& instructionSet);
+    binder::Status linkFile(const std::string& packageName, const std::string& relativePath,
+                            const std::string& fromBase, const std::string& toBase);
+    binder::Status moveAb(const std::string& packageName, const std::string& apkPath,
+                          const std::string& instructionSet, const std::string& outputPath);
+    binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath,
+                              const std::string& instructionSet,
+                              const std::optional<std::string>& outputPath, int64_t* _aidl_return);
+    binder::Status installApkVerity(const std::string& packageName, const std::string& filePath,
+                                    android::base::unique_fd verityInput, int32_t contentSize);
+    binder::Status assertFsverityRootHashMatches(const std::string& packageName,
+                                                 const std::string& filePath,
+                                                 const std::vector<uint8_t>& expectedHash);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
         const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 637a9f2..684a311 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -56,7 +56,7 @@
             @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
 
     // Returns false if it is cancelled. Returns true if it is completed or have other errors.
-    boolean dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+    boolean dexopt(@utf8InCpp String apkPath, int uid, @utf8InCpp String packageName,
             @utf8InCpp String instructionSet, int dexoptNeeded,
             @nullable @utf8InCpp String outputPath, int dexFlags,
             @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
@@ -85,21 +85,22 @@
             @utf8InCpp String profileName, @utf8InCpp String classpath);
     void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
 
-    void rmPackageDir(@utf8InCpp String packageDir);
-    void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
-            long cacheReservedBytes, int flags);
+    void rmPackageDir(@utf8InCpp String packageName, @utf8InCpp String packageDir);
+    void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, int flags);
     void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
             @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
-    void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
-    void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase,
-            @utf8InCpp String toBase);
-    void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
-            @utf8InCpp String outputPath);
-    long deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
-            @nullable @utf8InCpp String outputPath);
-    void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput,
-            int contentSize);
-    void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash);
+    void createOatDir(@utf8InCpp String packageName, @utf8InCpp String oatDir,
+            @utf8InCpp String instructionSet);
+    void linkFile(@utf8InCpp String packageName, @utf8InCpp String relativePath,
+            @utf8InCpp String fromBase, @utf8InCpp String toBase);
+    void moveAb(@utf8InCpp String packageName, @utf8InCpp String apkPath,
+            @utf8InCpp String instructionSet, @utf8InCpp String outputPath);
+    long deleteOdex(@utf8InCpp String packageName, @utf8InCpp String apkPath,
+            @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath);
+    void installApkVerity(@utf8InCpp String packageName, @utf8InCpp String filePath,
+            in FileDescriptor verityInput, int contentSize);
+    void assertFsverityRootHashMatches(@utf8InCpp String packageName, @utf8InCpp String filePath,
+            in byte[] expectedHash);
 
     boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
         int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
@@ -139,6 +140,8 @@
     const int FLAG_FREE_CACHE_V2 = 0x100;
     const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200;
     const int FLAG_FREE_CACHE_NOOP = 0x400;
+    // Set below flag to clear cache irrespective of target free bytes required
+    const int FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES = 0x800;
 
     const int FLAG_USE_QUOTA = 0x1000;
     const int FLAG_FORCE = 0x2000;
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 72f5f4b..4976646 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -42,6 +42,7 @@
 
 #define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2
 #define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA
+#define FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES InstalldNativeService::FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES
 
 int get_property(const char *key, char *value, const char *default_value) {
     return property_get(key, value, default_value);
@@ -146,7 +147,7 @@
     EXPECT_EQ(0, exists("com.example/cache/foo/one"));
     EXPECT_EQ(0, exists("com.example/cache/foo/two"));
 
-    service->freeCache(testUuid, kTbInBytes, 0,
+    service->freeCache(testUuid, kTbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(0, exists("com.example/normal"));
@@ -154,6 +155,62 @@
     EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
 }
 
+TEST_F(CacheTest, FreeCache_NonAggressive) {
+    LOG(INFO) << "FreeCache_NonAggressive";
+
+    mkdir("com.example");
+    touch("com.example/normal", 1 * kMbInBytes, 60);
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/one", 65 * kMbInBytes, 60);
+    touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+}
+
+TEST_F(CacheTest, FreeCache_DefyTargetFreeBytes) {
+    LOG(INFO) << "FreeCache_DefyTargetFreeBytes";
+
+    mkdir("com.example");
+    touch("com.example/normal", 1 * kMbInBytes, 60);
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/one", 65 * kMbInBytes, 60);
+    touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, kMbInBytes, FLAG_FREE_CACHE_V2
+            | FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, kMbInBytes, FLAG_FREE_CACHE_V2
+            | FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+}
+
 TEST_F(CacheTest, FreeCache_Age) {
     LOG(INFO) << "FreeCache_Age";
 
@@ -163,13 +220,13 @@
     touch("com.example/cache/foo/one", kMbInBytes, 60);
     touch("com.example/cache/foo/two", kMbInBytes, 120);
 
-    service->freeCache(testUuid, free() + kKbInBytes, 0,
+    service->freeCache(testUuid, free() + kKbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
     EXPECT_EQ(0, exists("com.example/cache/foo/two"));
 
-    service->freeCache(testUuid, free() + kKbInBytes, 0,
+    service->freeCache(testUuid, free() + kKbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
@@ -197,7 +254,7 @@
     EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
     EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
 
-    service->freeCache(testUuid, kTbInBytes, 0,
+    service->freeCache(testUuid, kTbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -219,7 +276,7 @@
 
     setxattr("com.example/cache/foo", "user.cache_group");
 
-    service->freeCache(testUuid, free() + kKbInBytes, 0,
+    service->freeCache(testUuid, free() + kKbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -264,7 +321,7 @@
     setxattr("com.example/cache/tomb", "user.cache_tombstone");
     setxattr("com.example/cache/tomb/group", "user.cache_group");
 
-    service->freeCache(testUuid, free() + kKbInBytes, 0,
+    service->freeCache(testUuid, free() + kKbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
@@ -285,7 +342,7 @@
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
 
-    service->freeCache(testUuid, free() + kKbInBytes, 0,
+    service->freeCache(testUuid, free() + kKbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, size("com.example/cache/group/file1"));
@@ -306,7 +363,7 @@
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
 
-    service->freeCache(testUuid, kTbInBytes, 0,
+    service->freeCache(testUuid, kTbInBytes,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, size("com.example/cache/group/file1"));
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index a937436..bb36c39 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -635,6 +635,7 @@
 
         int64_t bytes_freed;
         binder::Status result = service_->deleteOdex(
+            package_name_,
             apk_path_,
             kRuntimeIsa,
             in_dalvik_cache ? std::nullopt : std::make_optional<std::string>(app_oat_dir_.c_str()),
@@ -729,7 +730,7 @@
 
 TEST_F(DexoptTest, DexoptPrimaryPublicCreateOatDir) {
     LOG(INFO) << "DexoptPrimaryPublic";
-    ASSERT_BINDER_SUCCESS(service_->createOatDir(app_oat_dir_, kRuntimeIsa));
+    ASSERT_BINDER_SUCCESS(service_->createOatDir(package_name_, app_oat_dir_, kRuntimeIsa));
     CompilePrimaryDexOk("verify",
                         DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
                         app_oat_dir_.c_str(),
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index cfd42fe..3f7c7d6 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -528,7 +528,7 @@
 void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t,
         layer_id id, const LayerStackChange& lsc) {
     ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
-    t.setLayerStack(mLayers[id], lsc.layer_stack());
+    t.setLayerStack(mLayers[id], ui::LayerStack::fromValue(lsc.layer_stack()));
 }
 
 void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t,
@@ -566,7 +566,7 @@
 
 void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t,
         display_id id, const LayerStackChange& lsc) {
-    t.setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+    t.setDisplayLayerStack(mDisplays[id], ui::LayerStack::fromValue(lsc.layer_stack()));
 }
 
 void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t,
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index af8f718..931c5e3 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -243,11 +243,3 @@
     src: "handheld_core_hardware.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
-
-prebuilt_etc {
-    name: "android.software.app_compat_overrides.xml",
-    product_specific: true,
-    sub_dir: "permissions",
-    src: "android.software.app_compat_overrides.xml",
-    filename_from_src: true,
-}
diff --git a/data/etc/android.hardware.telephony.calling.xml b/data/etc/android.hardware.telephony.calling.xml
new file mode 100644
index 0000000..3e92a66
--- /dev/null
+++ b/data/etc/android.hardware.telephony.calling.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Calling API. -->
+<permissions>
+    <feature name="android.hardware.telephony" />
+    <feature name="android.software.telecom" />
+    <feature name="android.hardware.telephony.radio" />
+    <feature name="android.hardware.telephony.subscription" />
+    <feature name="android.hardware.telephony.calling" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml
index 082378d..c81cf06 100644
--- a/data/etc/android.hardware.telephony.cdma.xml
+++ b/data/etc/android.hardware.telephony.cdma.xml
@@ -4,9 +4,9 @@
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-  
+
           http://www.apache.org/licenses/LICENSE-2.0
-  
+
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,5 +17,11 @@
 <!-- This is the standard set of telephony features for a CDMA phone. -->
 <permissions>
     <feature name="android.hardware.telephony" />
+    <feature name="android.software.telecom" />
+    <feature name="android.hardware.telephony.radio" />
+    <feature name="android.hardware.telephony.subscription" />
     <feature name="android.hardware.telephony.cdma" />
+    <feature name="android.hardware.telephony.calling" />
+    <feature name="android.hardware.telephony.data" />
+    <feature name="android.hardware.telephony.messaging" />
 </permissions>
diff --git a/data/etc/android.hardware.telephony.data.xml b/data/etc/android.hardware.telephony.data.xml
new file mode 100644
index 0000000..2716152
--- /dev/null
+++ b/data/etc/android.hardware.telephony.data.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Data API. -->
+<permissions>
+    <feature name="android.hardware.telephony" />
+    <feature name="android.hardware.telephony.radio" />
+    <feature name="android.hardware.telephony.subscription" />
+    <feature name="android.hardware.telephony.data" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml
index 7927fa8..e368a06 100644
--- a/data/etc/android.hardware.telephony.gsm.xml
+++ b/data/etc/android.hardware.telephony.gsm.xml
@@ -4,9 +4,9 @@
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-  
+
           http://www.apache.org/licenses/LICENSE-2.0
-  
+
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,5 +17,11 @@
 <!-- This is the standard set of telephony features for a GSM phone. -->
 <permissions>
     <feature name="android.hardware.telephony" />
+    <feature name="android.software.telecom" />
+    <feature name="android.hardware.telephony.radio" />
+    <feature name="android.hardware.telephony.subscription" />
     <feature name="android.hardware.telephony.gsm" />
+    <feature name="android.hardware.telephony.calling" />
+    <feature name="android.hardware.telephony.data" />
+    <feature name="android.hardware.telephony.messaging" />
 </permissions>
diff --git a/data/etc/android.hardware.telephony.radio.xml b/data/etc/android.hardware.telephony.radio.xml
new file mode 100644
index 0000000..4377705
--- /dev/null
+++ b/data/etc/android.hardware.telephony.radio.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Radio Access API. -->
+<permissions>
+  <feature name="android.hardware.telephony" />
+  <feature name="android.hardware.telephony.radio" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.subscription.xml b/data/etc/android.hardware.telephony.subscription.xml
new file mode 100644
index 0000000..449a2a6
--- /dev/null
+++ b/data/etc/android.hardware.telephony.subscription.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Subscription API. -->
+<permissions>
+  <feature name="android.hardware.telephony" />
+  <feature name="android.hardware.telephony.subscription" />
+</permissions>
diff --git a/data/etc/android.hardware.type.automotive.xml b/data/etc/android.hardware.type.automotive.xml
index a9b4b05..113945b 100644
--- a/data/etc/android.hardware.type.automotive.xml
+++ b/data/etc/android.hardware.type.automotive.xml
@@ -17,4 +17,6 @@
 <!-- These features determine that the device running android is a car. -->
 <permissions>
     <feature name="android.hardware.type.automotive" />
+    <!-- TODO: Revert this after enabling work profiles refer b/170332519 -->
+    <unavailable-feature name="android.software.managed_users"/>
 </permissions>
diff --git a/data/etc/android.software.telecom.xml b/data/etc/android.software.telecom.xml
new file mode 100644
index 0000000..db28ba6
--- /dev/null
+++ b/data/etc/android.software.telecom.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard set of features for devices that support the Telecom API. -->
+<permissions>
+    <feature name="android.software.telecom" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index cc0ee82..95b8110 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -38,7 +38,7 @@
     <feature name="android.hardware.security.model.compatible" />
 
     <!-- basic system services -->
-    <feature name="android.software.connectionservice" />
+    <feature name="android.software.telecom" />
     <feature name="android.software.voice_recognizers" notLowRam="true" />
     <feature name="android.software.home_screen" />
     <feature name="android.software.companion_device_setup" />
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index e6db4ad..d601931 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -37,7 +37,7 @@
     <feature name="android.hardware.security.model.compatible" />
 
     <!-- basic system services -->
-    <feature name="android.software.connectionservice" />
+    <feature name="android.software.telecom" />
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
     <feature name="android.software.input_methods" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 41f1514..68b8def 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -42,7 +42,7 @@
 
     <!-- basic system services -->
     <feature name="android.software.app_widgets" />
-    <feature name="android.software.connectionservice" />
+    <feature name="android.software.telecom" />
     <feature name="android.software.voice_recognizers" notLowRam="true" />
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index b743f49..0389e57 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -39,6 +39,12 @@
  */
 typedef struct AChoreographer AChoreographer;
 
+struct AChoreographerFrameCallbackData;
+/**
+ * Opaque type that provides access to an AChoreographerFrameCallbackData object.
+ */
+typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData;
+
 /**
  * Prototype of the function that is called when a new frame is being rendered.
  * It's passed the time that the frame is being rendered as nanoseconds in the
@@ -60,6 +66,14 @@
 typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
 
 /**
+ * Prototype of the function that is called when a new frame is being rendered.
+ * It's passed the frame data that should not outlive the callback, as well as the data pointer
+ * provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_extendedFrameCallback)(
+        const AChoreographerFrameCallbackData* callbackData, void* data);
+
+/**
  * Prototype of the function that is called when the display refresh rate
  * changes. It's passed the new vsync period in nanoseconds, as well as the data
  * pointer provided by the application that registered a callback.
@@ -111,6 +125,14 @@
                                                uint32_t delayMillis) __INTRODUCED_IN(29);
 
 /**
+ * Posts a callback to run on the next frame. The data pointer provided will
+ * be passed to the callback function when it's called.
+ */
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+                                        AChoreographer_extendedFrameCallback callback, void* data)
+        __INTRODUCED_IN(33);
+
+/**
  * Registers a callback to be run when the display refresh rate changes. The
  * data pointer provided will be passed to the callback function when it's
  * called. The same callback may be registered multiple times, provided that a
@@ -160,6 +182,42 @@
                                                   AChoreographer_refreshRateCallback, void* data)
         __INTRODUCED_IN(30);
 
+/**
+ * The time in nanoseconds when the frame started being rendered.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+        const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * The number of possible frame timelines.
+ */
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+        const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * Get index of the platform-preferred FrameTimeline.
+ */
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+        const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * The vsync ID token used to map Choreographer data.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+        const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
+/**
+ * The time in nanoseconds which the frame at given index is expected to be presented.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+        const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
+/**
+ * The time in nanoseconds which the frame at given index needs to be ready by.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+        const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
 __END_DECLS
 
 #endif // ANDROID_CHOREOGRAPHER_H
diff --git a/include/android/input.h b/include/android/input.h
index 7642215..fbd61b5 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -169,6 +169,9 @@
 
     /** Drag event */
     AINPUT_EVENT_TYPE_DRAG = 5,
+
+    /** TouchMode event */
+    AINPUT_EVENT_TYPE_TOUCH_MODE = 6,
 };
 
 /**
@@ -1382,6 +1385,17 @@
  */
 void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
 
+/**
+ * Returns the AInputQueue* object associated with the supplied Java InputQueue
+ * object. The returned native object holds a weak reference to the Java object,
+ * and is only valid as long as the Java object has not yet been disposed. You
+ * should ensure that there is a strong reference to the Java object and that it
+ * has not been disposed before using the returned object.
+ *
+ * Available since API level 33.
+ */
+AInputQueue* AInputQueue_fromJava(JNIEnv* env, jobject inputQueue) __INTRODUCED_IN(33);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 059bc41..3a13104 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -595,6 +595,15 @@
                                                bool enableBackPressure)
                                                __INTRODUCED_IN(31);
 
+/**
+ * Sets the frame timeline to use.
+ *
+ * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to
+ * the corresponding expected present time and deadline from the frame to be rendered.
+ */
+void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
+                                          int64_t vsyncId) __INTRODUCED_IN(33);
+
 __END_DECLS
 
 #endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h
index 27c8476..ae70831 100644
--- a/include/ftl/Flags.h
+++ b/include/ftl/Flags.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,79 +14,22 @@
  * limitations under the License.
  */
 
-#include <android-base/stringprintf.h>
+#pragma once
 
-#include <array>
+#include <ftl/enum.h>
+#include <ftl/string.h>
+
 #include <cstdint>
-#include <optional>
+#include <iterator>
 #include <string>
 #include <type_traits>
 
-#include <ftl/NamedEnum.h>
 #include "utils/BitSet.h"
 
-#pragma once
+// TODO(b/185536303): Align with FTL style and namespace.
 
 namespace android {
 
-namespace details {
-
-template <typename F>
-inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
-
-template <typename F, typename T, T... I>
-constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
-    constexpr size_t count = seq.size();
-
-    std::array<F, count> values{};
-    for (size_t i = 0, v = 0; v < count; ++i) {
-        values[v++] = static_cast<F>(T{1} << i);
-    }
-
-    return values;
-}
-
-template <typename F>
-inline constexpr auto flag_values = generate_flag_values<F>(
-        std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
-
-template <typename F, std::size_t... I>
-constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
-    return std::array<std::optional<std::string_view>, sizeof...(I)>{
-            {enum_value_name<F, flag_values<F>[I]>()...}};
-}
-
-template <typename F>
-inline constexpr auto flag_names =
-        generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
-
-// A trait for determining whether a type is specifically an enum class or not.
-template <typename T, bool = std::is_enum_v<T>>
-struct is_enum_class : std::false_type {};
-
-// By definition, an enum class is an enum that is not implicitly convertible to its underlying
-// type.
-template <typename T>
-struct is_enum_class<T, true>
-      : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
-
-template <typename T>
-inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
-} // namespace details
-
-template <auto V>
-constexpr auto flag_name() {
-    using F = decltype(V);
-    return details::enum_value_name<F, V>();
-}
-
-template <typename F>
-constexpr std::optional<std::string_view> flag_name(F flag) {
-    using U = std::underlying_type_t<F>;
-    auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
-    return details::flag_names<F>[idx];
-}
-
 /* A class for handling flags defined by an enum or enum class in a type-safe way. */
 template <typename F>
 class Flags {
@@ -94,7 +37,7 @@
     // further to avoid this restriction but in general we want to encourage the use of enums
     // anyways.
     static_assert(std::is_enum_v<F>, "Flags type must be an enum");
-    using U = typename std::underlying_type_t<F>;
+    using U = std::underlying_type_t<F>;
 
 public:
     constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
@@ -106,11 +49,10 @@
     // should force them to be explicitly constructed from their underlying types to make full use
     // of the type checker.
     template <typename T = U>
-    constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
-          : mFlags(t) {}
+    constexpr Flags(T t, std::enable_if_t<!ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {}
+
     template <typename T = U>
-    explicit constexpr Flags(T t,
-                             typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
+    explicit constexpr Flags(T t, std::enable_if_t<ftl::is_scoped_enum_v<F>, T>* = nullptr)
           : mFlags(t) {}
 
     class Iterator {
@@ -229,16 +171,16 @@
         bool first = true;
         U unstringified = 0;
         for (const F f : *this) {
-            std::optional<std::string_view> flagString = flag_name(f);
-            if (flagString) {
-                appendFlag(result, flagString.value(), first);
+            if (const auto flagName = ftl::flag_name(f)) {
+                appendFlag(result, flagName.value(), first);
             } else {
                 unstringified |= static_cast<U>(f);
             }
         }
 
         if (unstringified != 0) {
-            appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
+            constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex;
+            appendFlag(result, ftl::to_string(unstringified, radix), first);
         }
 
         if (first) {
@@ -265,15 +207,14 @@
 // as flags. In order to use these, add them via a `using namespace` declaration.
 namespace flag_operators {
 
-template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
 inline Flags<F> operator~(F f) {
-    using U = typename std::underlying_type_t<F>;
-    return static_cast<F>(~static_cast<U>(f));
+    return static_cast<F>(~ftl::enum_cast(f));
 }
-template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+
+template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
 Flags<F> operator|(F lhs, F rhs) {
-    using U = typename std::underlying_type_t<F>;
-    return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
+    return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs));
 }
 
 } // namespace flag_operators
diff --git a/include/ftl/NamedEnum.h b/include/ftl/NamedEnum.h
deleted file mode 100644
index 6e98fee..0000000
--- a/include/ftl/NamedEnum.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/stringprintf.h>
-
-#include <array>
-#include <cstdint>
-#include <optional>
-#include <string>
-
-#pragma once
-
-namespace android {
-
-namespace details {
-template <typename E, E V>
-constexpr std::optional<std::string_view> enum_value_name() {
-    // Should look something like (but all on one line):
-    //   std::optional<std::string_view>
-    //   android::details::enum_value_name()
-    //   [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
-    std::string_view view = __PRETTY_FUNCTION__;
-    size_t templateStart = view.rfind("[");
-    size_t templateEnd = view.rfind("]");
-    if (templateStart == std::string::npos || templateEnd == std::string::npos) {
-        return std::nullopt;
-    }
-
-    // Extract the template parameters without the enclosing braces.
-    // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
-    view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
-    size_t valStart = view.rfind("V = ");
-    if (valStart == std::string::npos) {
-        return std::nullopt;
-    }
-
-    // Example (cont'd): V = android::test::TestEnums::ONE
-    view = view.substr(valStart);
-    // Check invalid enum values with cast, like V = (android::test::TestEnums)8.
-    if (view.find('(') != std::string::npos) {
-        return std::nullopt;
-    }
-    size_t nameStart = view.rfind("::");
-    if (nameStart == std::string::npos) {
-        return std::nullopt;
-    }
-
-    // Chop off the initial "::"
-    nameStart += 2;
-    return view.substr(nameStart);
-}
-
-template <typename E, typename T, T... I>
-constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
-    constexpr size_t count = seq.size();
-
-    std::array<E, count> values{};
-    for (size_t i = 0, v = 0; v < count; ++i) {
-        values[v++] = static_cast<E>(T{0} + i);
-    }
-
-    return values;
-}
-
-template <typename E, std::size_t N>
-inline constexpr auto enum_values =
-        generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});
-
-template <typename E, std::size_t N, std::size_t... I>
-constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
-    return std::array<std::optional<std::string_view>, sizeof...(I)>{
-            {enum_value_name<E, enum_values<E, N>[I]>()...}};
-}
-
-template <typename E, std::size_t N>
-inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});
-
-} // namespace details
-
-class NamedEnum {
-public:
-    // By default allowed enum value range is 0 ~ 7.
-    template <typename E>
-    static constexpr size_t max = 8;
-
-    template <auto V>
-    static constexpr auto enum_name() {
-        using E = decltype(V);
-        return details::enum_value_name<E, V>();
-    }
-
-    template <typename E>
-    static constexpr std::optional<std::string_view> enum_name(E val) {
-        auto idx = static_cast<size_t>(val);
-        return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
-    }
-
-    // Helper function for parsing enum value to string.
-    // Example : enum class TestEnums { ZERO = 0x0 };
-    // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
-    // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
-    // it should be declared to specialized the maximum enum by below:
-    // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
-    // If the enum class definition is sparse and contains enum values starting from a large value,
-    // Do not specialize it to a large number to avoid performance issues.
-    // The recommended maximum enum number to specialize is 64.
-    template <typename E>
-    static const std::string string(E val, const char* fallbackFormat = "%02d") {
-        std::string result;
-        std::optional<std::string_view> enumString = enum_name(val);
-        result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
-        return result;
-    }
-};
-
-} // namespace android
diff --git a/include/ftl/cast.h b/include/ftl/cast.h
new file mode 100644
index 0000000..ff1b58a
--- /dev/null
+++ b/include/ftl/cast.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <limits>
+#include <type_traits>
+
+#include <ftl/details/cast.h>
+
+namespace android::ftl {
+
+enum class CastSafety { kSafe, kUnderflow, kOverflow };
+
+// Returns whether static_cast<R>(v) is safe, or would result in underflow or overflow.
+//
+//   static_assert(ftl::cast_safety<uint8_t>(-1) == ftl::CastSafety::kUnderflow);
+//   static_assert(ftl::cast_safety<int8_t>(128u) == ftl::CastSafety::kOverflow);
+//
+//   static_assert(ftl::cast_safety<uint32_t>(-.1f) == ftl::CastSafety::kUnderflow);
+//   static_assert(ftl::cast_safety<int32_t>(static_cast<float>(INT32_MAX)) ==
+//                                           ftl::CastSafety::kOverflow);
+//
+//   static_assert(ftl::cast_safety<float>(-DBL_MAX) == ftl::CastSafety::kUnderflow);
+//
+template <typename R, typename T>
+constexpr CastSafety cast_safety(T v) {
+  static_assert(std::is_arithmetic_v<T>);
+  static_assert(std::is_arithmetic_v<R>);
+
+  constexpr bool kFromSigned = std::is_signed_v<T>;
+  constexpr bool kToSigned = std::is_signed_v<R>;
+
+  using details::max_exponent;
+
+  // If the R range contains the T range, then casting is always safe.
+  if constexpr ((kFromSigned == kToSigned && max_exponent<R> >= max_exponent<T>) ||
+                (!kFromSigned && kToSigned && max_exponent<R> > max_exponent<T>)) {
+    return CastSafety::kSafe;
+  }
+
+  using C = std::common_type_t<R, T>;
+
+  if constexpr (kFromSigned) {
+    using L = details::safe_limits<R, T>;
+
+    if constexpr (kToSigned) {
+      // Signed to signed.
+      if (v < L::lowest()) return CastSafety::kUnderflow;
+      return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
+    } else {
+      // Signed to unsigned.
+      if (v < 0) return CastSafety::kUnderflow;
+      return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
+                                                           : CastSafety::kOverflow;
+    }
+  } else {
+    using L = std::numeric_limits<R>;
+
+    if constexpr (kToSigned) {
+      // Unsigned to signed.
+      return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
+                                                           : CastSafety::kOverflow;
+    } else {
+      // Unsigned to unsigned.
+      return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
+    }
+  }
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/details/cast.h b/include/ftl/details/cast.h
new file mode 100644
index 0000000..87b9f1e
--- /dev/null
+++ b/include/ftl/details/cast.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <limits>
+#include <type_traits>
+
+namespace android::ftl::details {
+
+// Exponent whose power of 2 is the (exclusive) upper bound of T.
+template <typename T, typename L = std::numeric_limits<T>>
+constexpr int max_exponent = std::is_floating_point_v<T> ? L::max_exponent : L::digits;
+
+// Extension of std::numeric_limits<T> that reduces the maximum for integral types T such that it
+// has an exact representation for floating-point types F. For example, the maximum int32_t value
+// is 2'147'483'647, but casting it to float commonly rounds up to 2'147'483'650.f, which cannot
+// be safely converted back lest the signed overflow invokes undefined behavior. This pitfall is
+// avoided by clearing the lower (31 - 24 =) 7 bits of precision to 2'147'483'520. Note that the
+// minimum is representable.
+template <typename T, typename F>
+struct safe_limits : std::numeric_limits<T> {
+  static constexpr T max() {
+    using Base = std::numeric_limits<T>;
+
+    if constexpr (std::is_integral_v<T> && std::is_floating_point_v<F>) {
+      // Assume the mantissa is 24 bits for float, or 53 bits for double.
+      using Float = std::numeric_limits<F>;
+      static_assert(Float::is_iec559);
+
+      // If the integer is wider than the mantissa, clear the excess bits of precision.
+      constexpr int kShift = Base::digits - Float::digits;
+      if constexpr (kShift > 0) {
+        using U = std::make_unsigned_t<T>;
+        constexpr U kOne = static_cast<U>(1);
+        return static_cast<U>(Base::max()) & ~((kOne << kShift) - kOne);
+      }
+    }
+
+    return Base::max();
+  }
+};
+
+}  // namespace android::ftl::details
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
new file mode 100644
index 0000000..dfe3a09
--- /dev/null
+++ b/include/ftl/enum.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <limits>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/string.h>
+
+// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the
+// compiler-generated string literal for the signature of this function. The function is defined in
+// the global namespace with a short name and inferred return type to reduce bloat in the read-only
+// data segment.
+template <typename E, E V>
+constexpr auto ftl_enum() {
+  static_assert(std::is_enum_v<E>);
+
+  using R = std::optional<std::string_view>;
+  using namespace std::literals;
+
+  // The "pretty" signature has the following format:
+  //
+  //   auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue]
+  //
+  std::string_view view = __PRETTY_FUNCTION__;
+  const auto template_begin = view.rfind('[');
+  const auto template_end = view.rfind(']');
+  if (template_begin == view.npos || template_end == view.npos) return R{};
+
+  // Extract the template parameters without the enclosing brackets. Example (cont'd):
+  //
+  //   E = android::test::Enum, V = android::test::Enum::kValue
+  //
+  view = view.substr(template_begin + 1, template_end - template_begin - 1);
+  const auto value_begin = view.rfind("V = "sv);
+  if (value_begin == view.npos) return R{};
+
+  // Example (cont'd):
+  //
+  //   V = android::test::Enum::kValue
+  //
+  view = view.substr(value_begin);
+  const auto name_begin = view.rfind("::"sv);
+  if (name_begin == view.npos) return R{};
+
+  // Chop off the leading "::".
+  const auto name = view.substr(name_begin + 2);
+
+  // A value that is not enumerated has the format "Enum)42".
+  return name.find(')') == view.npos ? R{name} : R{};
+}
+
+namespace android::ftl {
+
+// Trait for determining whether a type is specifically a scoped enum or not. By definition, a
+// scoped enum is one that is not implicitly convertible to its underlying type.
+//
+// TODO: Replace with std::is_scoped_enum in C++23.
+//
+template <typename T, bool = std::is_enum_v<T>>
+struct is_scoped_enum : std::false_type {};
+
+template <typename T>
+struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {
+};
+
+template <typename T>
+inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
+
+// Shorthand for casting an enumerator to its integral value.
+//
+//   enum class E { A, B, C };
+//   static_assert(ftl::enum_cast(E::B) == 1);
+//
+template <typename E>
+constexpr auto enum_cast(E v) {
+  return static_cast<std::underlying_type_t<E>>(v);
+}
+
+// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named
+// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1
+// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the
+// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of-
+// range values results in undefined behavior if the underlying type is not fixed.
+//
+//   enum class E { A, B, C, F = 5, ftl_last = F };
+//
+//   static_assert(ftl::enum_begin_v<E> == E::A);
+//   static_assert(ftl::enum_last_v<E> == E::F);
+//   static_assert(ftl::enum_size_v<E> == 6);
+//
+//   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+//   static_assert(ftl::enum_begin_v<F> == F{0});
+//   static_assert(ftl::enum_last_v<F> == F{15});
+//   static_assert(ftl::enum_size_v<F> == 16);
+//
+template <typename E, typename = void>
+struct enum_begin {
+  static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator");
+  static constexpr E value{0};
+};
+
+template <typename E>
+struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> {
+  static constexpr E value = E::ftl_first;
+};
+
+template <typename E>
+inline constexpr E enum_begin_v = enum_begin<E>::value;
+
+template <typename E, typename = void>
+struct enum_end {
+  using U = std::underlying_type_t<E>;
+  static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator");
+
+  static constexpr E value{std::numeric_limits<U>::digits};
+};
+
+template <typename E>
+struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
+  static constexpr E value = E{enum_cast(E::ftl_last) + 1};
+};
+
+template <typename E>
+inline constexpr E enum_end_v = enum_end<E>::value;
+
+template <typename E>
+inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1};
+
+template <typename E>
+struct enum_size {
+  static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+  static constexpr auto kEnd = enum_cast(enum_end_v<E>);
+  static_assert(kBegin < kEnd, "Invalid range");
+
+  static constexpr std::size_t value = kEnd - kBegin;
+  static_assert(value <= 64, "Excessive range size");
+};
+
+template <typename E>
+inline constexpr std::size_t enum_size_v = enum_size<E>::value;
+
+namespace details {
+
+template <auto V>
+struct Identity {
+  static constexpr auto value = V;
+};
+
+template <typename E>
+using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>;
+
+template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>>
+struct EnumRange;
+
+template <typename E, template <E> class F, typename T, T... Vs>
+struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
+  static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+  static constexpr auto kSize = enum_size_v<E>;
+
+  using R = decltype(F<E{}>::value);
+  const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...};
+
+  constexpr const auto* begin() const { return values; }
+  constexpr const auto* end() const { return values + kSize; }
+};
+
+template <auto V>
+struct EnumName {
+  static constexpr auto value = ftl_enum<decltype(V), V>();
+};
+
+template <auto I>
+struct FlagName {
+  using E = decltype(I);
+  using U = std::underlying_type_t<E>;
+
+  static constexpr E V{U{1} << enum_cast(I)};
+  static constexpr auto value = ftl_enum<E, V>();
+};
+
+}  // namespace details
+
+// Returns an iterable over the range of an enum.
+//
+//   enum class E { A, B, C, F = 5, ftl_last = F };
+//
+//   std::string string;
+//   for (E v : ftl::enum_range<E>()) {
+//     string += ftl::enum_name(v).value_or("?");
+//   }
+//
+//   assert(string == "ABC??F");
+//
+template <typename E>
+constexpr auto enum_range() {
+  return details::EnumRange<E>{};
+}
+
+// Returns a stringified enumerator at compile time.
+//
+//   enum class E { A, B, C };
+//   static_assert(ftl::enum_name<E::B>() == "B");
+//
+template <auto V>
+constexpr std::string_view enum_name() {
+  constexpr auto kName = ftl_enum<decltype(V), V>();
+  static_assert(kName, "Unknown enumerator");
+  return *kName;
+}
+
+// Returns a stringified enumerator, possibly at compile time.
+//
+//   enum class E { A, B, C, F = 5, ftl_last = F };
+//
+//   static_assert(ftl::enum_name(E::C).value_or("?") == "C");
+//   static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+//
+template <typename E>
+constexpr std::optional<std::string_view> enum_name(E v) {
+  const auto value = enum_cast(v);
+
+  constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+  constexpr auto kLast = enum_cast(enum_last_v<E>);
+  if (value < kBegin || value > kLast) return {};
+
+  constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
+  return kRange.values[value - kBegin];
+}
+
+// Returns a stringified flag enumerator, possibly at compile time.
+//
+//   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+//   static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
+//   static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
+//
+template <typename E>
+constexpr std::optional<std::string_view> flag_name(E v) {
+  const auto value = enum_cast(v);
+
+  // TODO: Replace with std::popcount and std::countr_zero in C++20.
+  if (__builtin_popcountl(value) != 1) return {};
+
+  constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
+  return kRange.values[__builtin_ctzl(value)];
+}
+
+// Returns a stringified enumerator, or its integral value if not named.
+//
+//   enum class E { A, B, C, F = 5, ftl_last = F };
+//
+//   assert(ftl::enum_string(E::C) == "C");
+//   assert(ftl::enum_string(E{3}) == "3");
+//
+template <typename E>
+inline std::string enum_string(E v) {
+  if (const auto name = enum_name(v)) {
+    return std::string(*name);
+  }
+  return to_string(enum_cast(v));
+}
+
+// Returns a stringified flag enumerator, or its integral value if not named.
+//
+//   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+//   assert(ftl::flag_string(F::Z) == "Z");
+//   assert(ftl::flag_string(F{7}) == "0b111");
+//
+template <typename E>
+inline std::string flag_string(E v) {
+  if (const auto name = flag_name(v)) {
+    return std::string(*name);
+  }
+  constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
+  return to_string(enum_cast(v), radix);
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/initializer_list.h b/include/ftl/initializer_list.h
index 769c09f..2102c25 100644
--- a/include/ftl/initializer_list.h
+++ b/include/ftl/initializer_list.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <functional>
 #include <tuple>
 #include <utility>
 
@@ -65,18 +66,18 @@
   std::tuple<Types...> tuple;
 };
 
-template <typename K, typename V>
+template <typename K, typename V, typename KeyEqual = std::equal_to<K>>
 struct KeyValue {};
 
 // Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
 // value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
 // with the latter.
-template <typename K, typename V, std::size_t... Sizes, typename... Types>
-struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
+template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types>
+struct InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...> {
   // Accumulate the three arguments to std::pair's piecewise constructor.
   template <typename... Args>
   [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
-      KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
+      KeyValue<K, V, E>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
       std::tuple<K&&>, std::tuple<Args&&...>> {
     return {std::tuple_cat(
         std::move(tuple),
@@ -94,9 +95,9 @@
   return InitializerList<T>{}(std::forward<Args>(args)...);
 }
 
-template <typename K, typename V, typename... Args>
+template <typename K, typename V, typename E = std::equal_to<K>, typename... Args>
 [[nodiscard]] constexpr auto map(Args&&... args) {
-  return list<KeyValue<K, V>>(std::forward<Args>(args)...);
+  return list<KeyValue<K, V, E>>(std::forward<Args>(args)...);
 }
 
 template <typename K, typename V>
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
index 84c15eb..2effaa4 100644
--- a/include/ftl/small_map.h
+++ b/include/ftl/small_map.h
@@ -19,6 +19,7 @@
 #include <ftl/initializer_list.h>
 #include <ftl/small_vector.h>
 
+#include <algorithm>
 #include <functional>
 #include <optional>
 #include <type_traits>
@@ -28,7 +29,10 @@
 
 // Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
 // stored in contiguous storage for cache efficiency. The map is allocated statically until its size
-// exceeds N, at which point mappings are relocated to dynamic memory.
+// exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has
+// a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place
+// counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator,
+// but immutable getters that can optionally transform the value.
 //
 // SmallMap<K, V, 0> unconditionally allocates on the heap.
 //
@@ -43,18 +47,21 @@
 //   assert(!map.dynamic());
 //
 //   assert(map.contains(123));
-//   assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
+//   assert(map.get(42, [](const std::string& s) { return s.size(); }) == 3u);
 //
-//   const auto opt = map.find(-1);
+//   const auto opt = map.get(-1);
 //   assert(opt);
 //
 //   std::string& ref = *opt;
 //   assert(ref.empty());
 //   ref = "xyz";
 //
-//   assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+//   map.emplace_or_replace(0, "vanilla", 2u, 3u);
+//   assert(map.dynamic());
 //
-template <typename K, typename V, std::size_t N>
+//   assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
+//
+template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>>
 class SmallMap final {
   using Map = SmallVector<std::pair<const K, V>, N>;
 
@@ -80,12 +87,7 @@
   // The syntax for listing pairs is as follows:
   //
   //   ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
-  //
   //   static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
-  //   assert(map.size() == 3u);
-  //   assert(map.contains(-1) && map.find(-1)->get().empty());
-  //   assert(map.contains(42) && map.find(42)->get() == "???");
-  //   assert(map.contains(123) && map.find(123)->get() == "abc");
   //
   // The types of the key and value are deduced if the first pair contains exactly two arguments:
   //
@@ -95,7 +97,7 @@
   template <typename U, std::size_t... Sizes, typename... Types>
   SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list)
       : map_(std::move(list)) {
-    // TODO: Enforce unique keys.
+    deduplicate();
   }
 
   size_type max_size() const { return map_.max_size(); }
@@ -115,27 +117,27 @@
 
   // Returns whether a mapping exists for the given key.
   bool contains(const key_type& key) const {
-    return find(key, [](const mapped_type&) {});
+    return get(key, [](const mapped_type&) {});
   }
 
   // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
   //
   //   ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
   //
-  //   const auto opt = map.find('c');
+  //   const auto opt = map.get('c');
   //   assert(opt == 'C');
   //
   //   char d = 'd';
-  //   const auto ref = map.find('d').value_or(std::ref(d));
+  //   const auto ref = map.get('d').value_or(std::ref(d));
   //   ref.get() = 'D';
   //   assert(d == 'D');
   //
-  auto find(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
-    return find(key, [](const mapped_type& v) { return std::cref(v); });
+  auto get(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
+    return get(key, [](const mapped_type& v) { return std::cref(v); });
   }
 
-  auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
-    return find(key, [](mapped_type& v) { return std::ref(v); });
+  auto get(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
+    return get(key, [](mapped_type& v) { return std::ref(v); });
   }
 
   // Returns the result R of a unary operation F on (a constant or mutable reference to) the value
@@ -144,14 +146,14 @@
   //
   //   ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
   //
-  //   assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
-  //   assert(map.find('c', [](char& c) { c = std::toupper(c); }));
+  //   assert(map.get('c', [](char c) { return std::toupper(c); }) == 'Z');
+  //   assert(map.get('c', [](char& c) { c = std::toupper(c); }));
   //
   template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
-  auto find(const key_type& key, F f) const
+  auto get(const key_type& key, F f) const
       -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
     for (auto& [k, v] : *this) {
-      if (k == key) {
+      if (KeyEqual{}(k, key)) {
         if constexpr (std::is_void_v<R>) {
           f(v);
           return true;
@@ -165,28 +167,119 @@
   }
 
   template <typename F>
-  auto find(const key_type& key, F f) {
-    return std::as_const(*this).find(
+  auto get(const key_type& key, F f) {
+    return std::as_const(*this).get(
         key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); });
   }
 
+  // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise.
+  const_iterator find(const key_type& key) const { return const_cast<SmallMap&>(*this).find(key); }
+  iterator find(const key_type& key) { return find(key, begin()); }
+
+  // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping,
+  // and whether the mapping was inserted.
+  //
+  // On emplace, if the map reaches its static or dynamic capacity, then all iterators are
+  // invalidated. Otherwise, only the end() iterator is invalidated.
+  //
+  template <typename... Args>
+  std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) {
+    if (const auto it = find(key); it != end()) {
+      return {it, false};
+    }
+
+    auto& ref = map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key),
+                                  std::forward_as_tuple(std::forward<Args>(args)...));
+    return {&ref, true};
+  }
+
+  // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator
+  // otherwise.
+  //
+  // The value is replaced via move constructor, so type V does not need to define copy/move
+  // assignment, e.g. its data members may be const.
+  //
+  // The arguments may directly or indirectly refer to the mapping being replaced.
+  //
+  // Iterators to the replaced mapping point to its replacement, and others remain valid.
+  //
+  template <typename... Args>
+  iterator try_replace(const key_type& key, Args&&... args) {
+    const auto it = find(key);
+    if (it == end()) return it;
+    map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key),
+                 std::forward_as_tuple(std::forward<Args>(args)...));
+    return it;
+  }
+
+  // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or
+  // false on replace.
+  //
+  // The value is emplaced and replaced via move constructor, so type V does not need to define
+  // copy/move assignment, e.g. its data members may be const.
+  //
+  // On emplace, if the map reaches its static or dynamic capacity, then all iterators are
+  // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators
+  // to the replaced mapping point to its replacement, and others remain valid.
+  //
+  template <typename... Args>
+  std::pair<iterator, bool> emplace_or_replace(const key_type& key, Args&&... args) {
+    const auto [it, ok] = try_emplace(key, std::forward<Args>(args)...);
+    if (ok) return {it, ok};
+    map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key),
+                 std::forward_as_tuple(std::forward<Args>(args)...));
+    return {it, ok};
+  }
+
+  // Removes a mapping if it exists, and returns whether it did.
+  //
+  // The last() and end() iterators, as well as those to the erased mapping, are invalidated.
+  //
+  bool erase(const key_type& key) { return erase(key, begin()); }
+
+  // Removes all mappings.
+  //
+  // All iterators are invalidated.
+  //
+  void clear() { map_.clear(); }
+
  private:
+  iterator find(const key_type& key, iterator first) {
+    return std::find_if(first, end(),
+                        [&key](const auto& pair) { return KeyEqual{}(pair.first, key); });
+  }
+
+  bool erase(const key_type& key, iterator first) {
+    const auto it = find(key, first);
+    if (it == end()) return false;
+    map_.unstable_erase(it);
+    return true;
+  }
+
+  void deduplicate() {
+    for (auto it = begin(); it != end();) {
+      if (const auto key = it->first; ++it != end()) {
+        while (erase(key, it));
+      }
+    }
+  }
+
   Map map_;
 };
 
 // Deduction guide for in-place constructor.
-template <typename K, typename V, std::size_t... Sizes, typename... Types>
-SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
-    -> SmallMap<K, V, sizeof...(Sizes)>;
+template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types>
+SmallMap(InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...>&&)
+    -> SmallMap<K, V, sizeof...(Sizes), E>;
 
 // Returns whether the key-value pairs of two maps are equal.
-template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
-bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E>
+bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) {
   if (lhs.size() != rhs.size()) return false;
 
   for (const auto& [k, v] : lhs) {
     const auto& lv = v;
-    if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+    if (!rhs.get(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
       return false;
     }
   }
@@ -195,8 +288,8 @@
 }
 
 // TODO: Remove in C++20.
-template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
-inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E>
+inline bool operator!=(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) {
   return !(lhs == rhs);
 }
 
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index cb0ae35..65a9536 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -151,8 +151,6 @@
   DISPATCH(reference, back, noexcept)
   DISPATCH(const_reference, back, const)
 
-#undef DISPATCH
-
   reference operator[](size_type i) {
     return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i];
   }
@@ -214,13 +212,15 @@
   //
   // The last() and end() iterators are invalidated.
   //
-  void pop_back() {
-    if (dynamic()) {
-      std::get<Dynamic>(vector_).pop_back();
-    } else {
-      std::get<Static>(vector_).pop_back();
-    }
-  }
+  DISPATCH(void, pop_back, noexcept)
+
+  // Removes all elements.
+  //
+  // All iterators are invalidated.
+  //
+  DISPATCH(void, clear, noexcept)
+
+#undef DISPATCH
 
   // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
   // this moves the last element to the slot of the erased element.
@@ -345,10 +345,11 @@
     return true;
   }
 
+  using Impl::clear;
   using Impl::pop_back;
 
   void unstable_erase(iterator it) {
-    if (it != last()) std::iter_swap(it, last());
+    if (it != last()) replace(it, std::move(back()));
     pop_back();
   }
 
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
index 96a1ae8..cd7b92a 100644
--- a/include/ftl/static_vector.h
+++ b/include/ftl/static_vector.h
@@ -189,8 +189,7 @@
   }
 
   StaticVector& operator=(StaticVector&& other) {
-    std::destroy(begin(), end());
-    size_ = 0;
+    clear();
     swap<true>(other);
     return *this;
   }
@@ -280,6 +279,15 @@
   //
   void pop_back() { unstable_erase(last()); }
 
+  // Removes all elements.
+  //
+  // All iterators are invalidated.
+  //
+  void clear() {
+    std::destroy(begin(), end());
+    size_ = 0;
+  }
+
   // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
   // this moves the last element to the slot of the erased element.
   //
diff --git a/include/ftl/string.h b/include/ftl/string.h
new file mode 100644
index 0000000..2d96b06
--- /dev/null
+++ b/include/ftl/string.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <charconv>
+#include <limits>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+namespace android::ftl {
+
+enum class Radix { kBin = 2, kDec = 10, kHex = 16 };
+
+template <typename T>
+struct to_chars_length {
+  static_assert(std::is_integral_v<T>);
+  // Maximum binary digits, plus minus sign and radix prefix.
+  static constexpr std::size_t value = std::numeric_limits<std::make_unsigned_t<T>>::digits + 3;
+};
+
+template <typename T>
+constexpr std::size_t to_chars_length_v = to_chars_length<T>::value;
+
+template <typename T = std::int64_t>
+using to_chars_buffer_t = char[to_chars_length_v<T>];
+
+// Lightweight (not allocating nor sprintf-based) alternative to std::to_string for integers, with
+// optional radix. See also ftl::to_string below.
+//
+//   ftl::to_chars_buffer_t<> buffer;
+//
+//   assert(ftl::to_chars(buffer, 123u) == "123");
+//   assert(ftl::to_chars(buffer, -42, ftl::Radix::kBin) == "-0b101010");
+//   assert(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex) == "0xcafe");
+//   assert(ftl::to_chars(buffer, '*', ftl::Radix::kHex) == "0x2a");
+//
+template <typename T, std::size_t N>
+std::string_view to_chars(char (&buffer)[N], T v, Radix radix = Radix::kDec) {
+  static_assert(N >= to_chars_length_v<T>);
+
+  auto begin = buffer + 2;
+  const auto [end, err] = std::to_chars(begin, buffer + N, v, static_cast<int>(radix));
+  assert(err == std::errc());
+
+  if (radix == Radix::kDec) {
+    // TODO: Replace with {begin, end} in C++20.
+    return {begin, static_cast<std::size_t>(end - begin)};
+  }
+
+  const auto prefix = radix == Radix::kBin ? 'b' : 'x';
+  if constexpr (std::is_unsigned_v<T>) {
+    buffer[0] = '0';
+    buffer[1] = prefix;
+  } else {
+    if (*begin == '-') {
+      *buffer = '-';
+    } else {
+      --begin;
+    }
+
+    *begin-- = prefix;
+    *begin = '0';
+  }
+
+  // TODO: Replace with {buffer, end} in C++20.
+  return {buffer, static_cast<std::size_t>(end - buffer)};
+}
+
+// Lightweight (not sprintf-based) alternative to std::to_string for integers, with optional radix.
+//
+//   assert(ftl::to_string(123u) == "123");
+//   assert(ftl::to_string(-42, ftl::Radix::kBin) == "-0b101010");
+//   assert(ftl::to_string(0xcafe, ftl::Radix::kHex) == "0xcafe");
+//   assert(ftl::to_string('*', ftl::Radix::kHex) == "0x2a");
+//
+template <typename T>
+inline std::string to_string(T v, Radix radix = Radix::kDec) {
+  to_chars_buffer_t<T> buffer;
+  return std::string(to_chars(buffer, v, radix));
+}
+
+std::string to_string(bool) = delete;
+std::string to_string(bool, Radix) = delete;
+
+}  // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index a6213f3..9148fee 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -18,7 +18,8 @@
 #define _LIBINPUT_DISPLAY_VIEWPORT_H
 
 #include <android-base/stringprintf.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
+#include <ftl/string.h>
 #include <gui/constants.h>
 #include <input/Input.h>
 
@@ -44,6 +45,8 @@
     INTERNAL = 1,
     EXTERNAL = 2,
     VIRTUAL = 3,
+
+    ftl_last = VIRTUAL
 };
 
 /*
@@ -132,9 +135,8 @@
                             "physicalFrame=[%d, %d, %d, %d], "
                             "deviceSize=[%d, %d], "
                             "isActive=[%d]",
-                            NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
-                            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
-                                         : "<none>",
+                            ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
+                            physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
                             orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
                             physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
                             deviceHeight, isActive);
diff --git a/include/input/Input.h b/include/input/Input.h
index 4adaa5b..ddff144 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -201,8 +201,17 @@
 class Parcel;
 #endif
 
+/*
+ * Apply the given transform to the point without applying any translation/offset.
+ */
+vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy);
+
 const char* inputEventTypeToString(int32_t type);
 
+std::string inputEventSourceToString(int32_t source);
+
+bool isFromSource(uint32_t source, uint32_t test);
+
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
@@ -369,13 +378,10 @@
     float getAxisValue(int32_t axis) const;
     status_t setAxisValue(int32_t axis, float value);
 
-    void scale(float globalScale);
-
     // Scale the pointer coordinates according to a global scale and a
     // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
     // axes, however the window scaling will not.
     void scale(float globalScale, float windowXScale, float windowYScale);
-    void applyOffset(float xOffset, float yOffset);
 
     void transform(const ui::Transform& transform);
 
@@ -526,13 +532,17 @@
 
     inline int32_t getAction() const { return mAction; }
 
-    inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
+    static int32_t getActionMasked(int32_t action) { return action & AMOTION_EVENT_ACTION_MASK; }
 
-    inline int32_t getActionIndex() const {
-        return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
-                >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+    inline int32_t getActionMasked() const { return getActionMasked(mAction); }
+
+    static int32_t getActionIndex(int32_t action) {
+        return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+                AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
     }
 
+    inline int32_t getActionIndex() const { return getActionIndex(mAction); }
+
     inline void setAction(int32_t action) { mAction = action; }
 
     inline int32_t getFlags() const { return mFlags; }
@@ -561,7 +571,9 @@
 
     inline float getYOffset() const { return mTransform.ty(); }
 
-    inline ui::Transform getTransform() const { return mTransform; }
+    inline const ui::Transform& getTransform() const { return mTransform; }
+
+    int getSurfaceRotation() const;
 
     inline float getXPrecision() const { return mXPrecision; }
 
@@ -577,9 +589,7 @@
 
     void setCursorPosition(float x, float y);
 
-    uint32_t getDisplayOrientation() const { return mDisplayOrientation; }
-
-    int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; }
+    inline const ui::Transform& getRawTransform() const { return mRawTransform; }
 
     static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
 
@@ -755,8 +765,8 @@
                     int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
                     MotionClassification classification, const ui::Transform& transform,
                     float xPrecision, float yPrecision, float rawXCursorPosition,
-                    float rawYCursorPosition, uint32_t displayOrientation, int32_t displayWidth,
-                    int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+                    float rawYCursorPosition, const ui::Transform& rawTransform, nsecs_t downTime,
+                    nsecs_t eventTime, size_t pointerCount,
                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
 
     void copyFrom(const MotionEvent* other, bool keepHistory);
@@ -801,6 +811,14 @@
 
     static std::string actionToString(int32_t action);
 
+    // MotionEvent will transform various axes in different ways, based on the source. For
+    // example, the x and y axes will not have any offsets/translations applied if it comes from a
+    // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
+    // are used to apply these transformations for different axes.
+    static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
+    static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
+                                               const PointerCoords&);
+
 protected:
     int32_t mAction;
     int32_t mActionButton;
@@ -814,9 +832,7 @@
     float mYPrecision;
     float mRawXCursorPosition;
     float mRawYCursorPosition;
-    uint32_t mDisplayOrientation;
-    int32_t mDisplayWidth;
-    int32_t mDisplayHeight;
+    ui::Transform mRawTransform;
     nsecs_t mDownTime;
     Vector<PointerProperties> mPointerProperties;
     std::vector<nsecs_t> mSampleEventTimes;
@@ -834,15 +850,12 @@
 
     inline bool getHasFocus() const { return mHasFocus; }
 
-    inline bool getInTouchMode() const { return mInTouchMode; }
-
-    void initialize(int32_t id, bool hasFocus, bool inTouchMode);
+    void initialize(int32_t id, bool hasFocus);
 
     void initialize(const FocusEvent& from);
 
 protected:
     bool mHasFocus;
-    bool mInTouchMode;
 };
 
 /*
@@ -888,6 +901,25 @@
     float mX, mY;
 };
 
+/*
+ * Touch mode events.
+ */
+class TouchModeEvent : public InputEvent {
+public:
+    virtual ~TouchModeEvent() {}
+
+    virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_TOUCH_MODE; }
+
+    inline bool isInTouchMode() const { return mIsInTouchMode; }
+
+    void initialize(int32_t id, bool isInTouchMode);
+
+    void initialize(const TouchModeEvent& from);
+
+protected:
+    bool mIsInTouchMode;
+};
+
 /**
  * Base class for verified events.
  * Do not create a VerifiedInputEvent explicitly.
@@ -912,8 +944,8 @@
  */
 struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent {
     int32_t action;
-    nsecs_t downTimeNanos;
     int32_t flags;
+    nsecs_t downTimeNanos;
     int32_t keyCode;
     int32_t scanCode;
     int32_t metaState;
@@ -928,8 +960,8 @@
     float rawX;
     float rawY;
     int32_t actionMasked;
-    nsecs_t downTimeNanos;
     int32_t flags;
+    nsecs_t downTimeNanos;
     int32_t metaState;
     int32_t buttonState;
 };
@@ -952,6 +984,7 @@
     virtual FocusEvent* createFocusEvent() = 0;
     virtual CaptureEvent* createCaptureEvent() = 0;
     virtual DragEvent* createDragEvent() = 0;
+    virtual TouchModeEvent* createTouchModeEvent() = 0;
 };
 
 /*
@@ -968,6 +1001,7 @@
     virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
     virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; }
     virtual DragEvent* createDragEvent() override { return &mDragEvent; }
+    virtual TouchModeEvent* createTouchModeEvent() override { return &mTouchModeEvent; }
 
 private:
     KeyEvent mKeyEvent;
@@ -975,6 +1009,7 @@
     FocusEvent mFocusEvent;
     CaptureEvent mCaptureEvent;
     DragEvent mDragEvent;
+    TouchModeEvent mTouchModeEvent;
 };
 
 /*
@@ -990,6 +1025,7 @@
     virtual FocusEvent* createFocusEvent() override;
     virtual CaptureEvent* createCaptureEvent() override;
     virtual DragEvent* createDragEvent() override;
+    virtual TouchModeEvent* createTouchModeEvent() override;
 
     void recycle(InputEvent* event);
 
@@ -1001,6 +1037,7 @@
     std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
     std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
     std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
+    std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
 };
 
 /*
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 7f0324a..22aae19 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -84,6 +84,9 @@
     GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR,
     GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED,
     SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION,
+
+    ftl_first = ACCELEROMETER,
+    ftl_last = SIGNIFICANT_MOTION
 };
 
 enum class InputDeviceSensorAccuracy : int32_t {
@@ -105,6 +108,8 @@
     PLAYER_ID = 1,
     RGB = 2,
     MULTI_COLOR = 3,
+
+    ftl_last = MULTI_COLOR
 };
 
 struct InputDeviceSensorInfo {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index a790b56..edcb615 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -72,6 +72,9 @@
         CAPTURE,
         DRAG,
         TIMELINE,
+        TOUCH_MODE,
+
+        ftl_last = TOUCH_MODE
     };
 
     struct Header {
@@ -111,7 +114,7 @@
 
         struct Motion {
             int32_t eventId;
-            uint32_t empty1;
+            uint32_t pointerCount;
             nsecs_t eventTime __attribute__((aligned(8)));
             int32_t deviceId;
             int32_t source;
@@ -126,20 +129,22 @@
             uint8_t empty2[3];                   // 3 bytes to fill gap created by classification
             int32_t edgeFlags;
             nsecs_t downTime __attribute__((aligned(8)));
-            float dsdx;
-            float dtdx;
-            float dtdy;
-            float dsdy;
-            float tx;
-            float ty;
+            float dsdx; // Begin window transform
+            float dtdx; //
+            float dtdy; //
+            float dsdy; //
+            float tx;   //
+            float ty;   // End window transform
             float xPrecision;
             float yPrecision;
             float xCursorPosition;
             float yCursorPosition;
-            uint32_t displayOrientation;
-            int32_t displayWidth;
-            int32_t displayHeight;
-            uint32_t pointerCount;
+            float dsdxRaw; // Begin raw transform
+            float dtdxRaw; //
+            float dtdyRaw; //
+            float dsdyRaw; //
+            float txRaw;   //
+            float tyRaw;   // End raw transform
             /**
              * The "pointers" field must be the last field of the struct InputMessage.
              * When we send the struct InputMessage across the socket, we are not
@@ -173,10 +178,9 @@
 
         struct Focus {
             int32_t eventId;
-            // The following 3 fields take up 4 bytes total
+            // The following 2 fields take up 4 bytes total
             bool hasFocus;
-            bool inTouchMode;
-            uint8_t empty[2];
+            uint8_t empty[3];
 
             inline size_t size() const { return sizeof(Focus); }
         } focus;
@@ -206,6 +210,15 @@
 
             inline size_t size() const { return sizeof(Timeline); }
         } timeline;
+
+        struct TouchMode {
+            int32_t eventId;
+            // The following 2 fields take up 4 bytes total
+            bool isInTouchMode;
+            uint8_t empty[3];
+
+            inline size_t size() const { return sizeof(TouchMode); }
+        } touchMode;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -355,9 +368,8 @@
                                 int32_t metaState, int32_t buttonState,
                                 MotionClassification classification, const ui::Transform& transform,
                                 float xPrecision, float yPrecision, float xCursorPosition,
-                                float yCursorPosition, uint32_t displayOrientation,
-                                int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
-                                nsecs_t eventTime, uint32_t pointerCount,
+                                float yCursorPosition, const ui::Transform& rawTransform,
+                                nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
                                 const PointerProperties* pointerProperties,
                                 const PointerCoords* pointerCoords);
 
@@ -368,7 +380,7 @@
      * Returns DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode);
+    status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus);
 
     /* Publishes a capture event to the input channel.
      *
@@ -388,6 +400,15 @@
      */
     status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
 
+    /* Publishes a touch mode event to the input channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode);
+
     struct Finished {
         uint32_t seq;
         bool handled;
@@ -658,6 +679,7 @@
     static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
     static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
     static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
+    static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg);
     static void addSample(MotionEvent* event, const InputMessage* msg);
     static bool canAddSample(const Batch& batch, const InputMessage* msg);
     static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 2c6eacb..dfb0ff5 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -201,10 +201,8 @@
     std::array<std::atomic<HalSupport>,
                static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
             mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
-    // Android framework only sends mode upto DISPLAY_INACTIVE.
-    // Need to increase the array if more mode supported.
     std::array<std::atomic<HalSupport>,
-               static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+               static_cast<int32_t>(*(android::enum_range<hardware::power::Mode>().end() - 1)) + 1>
             mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
 };
 
diff --git a/include/private/surface_control_private.h b/include/private/surface_control_private.h
index 37a476e..7e6c515 100644
--- a/include/private/surface_control_private.h
+++ b/include/private/surface_control_private.h
@@ -29,8 +29,8 @@
 /**
  * Callback to be notified when surface stats for a specific surface control are available.
  */
-typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context,
-        ASurfaceControl* control, ASurfaceControlStats* stats);
+typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context, int32_t id,
+        ASurfaceControlStats* stats);
 
 /**
  * Registers a callback to be invoked when surface stats from a specific surface are available.
@@ -42,7 +42,7 @@
  *
  * \param func The callback to be invoked when surface stats are available.
  */
-void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context,
+void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, int32_t id, void* context,
         ASurfaceControl_SurfaceStatsListener func);
 
 /**
diff --git a/libs/battery/Android.bp b/libs/battery/Android.bp
new file mode 100644
index 0000000..c860324
--- /dev/null
+++ b/libs/battery/Android.bp
@@ -0,0 +1,37 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library {
+    name: "libbattery",
+    srcs: [
+        "LongArrayMultiStateCounter.cpp",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+    export_include_dirs: ["."],
+}
+
+cc_test {
+    name: "libbattery_test",
+    srcs: [
+        "MultiStateCounterTest.cpp",
+        "LongArrayMultiStateCounterTest.cpp",
+    ],
+    static_libs: ["libbattery"],
+    shared_libs: [
+        "liblog",
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
new file mode 100644
index 0000000..125cfaf
--- /dev/null
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LongArrayMultiStateCounter.h"
+#include <log/log.h>
+
+namespace android {
+namespace battery {
+
+template <>
+bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
+                                       const std::vector<uint64_t>& newValue,
+                                       std::vector<uint64_t>* outValue) const {
+    size_t size = previousValue.size();
+    if (newValue.size() != size) {
+        ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size);
+        return false;
+    }
+
+    bool is_delta_valid = true;
+    for (int i = size - 1; i >= 0; i--) {
+        if (newValue[i] >= previousValue[i]) {
+            (*outValue)[i] = newValue[i] - previousValue[i];
+        } else {
+            (*outValue)[i] = 0;
+            is_delta_valid = false;
+        }
+    }
+    return is_delta_valid;
+}
+
+template <>
+void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
+                                     const std::vector<uint64_t>& value2, const uint64_t numerator,
+                                     const uint64_t denominator) const {
+    if (numerator != denominator) {
+        for (int i = value2.size() - 1; i >= 0; i--) {
+            // The caller ensures that denominator != 0
+            (*value1)[i] += value2[i] * numerator / denominator;
+        }
+    } else {
+        for (int i = value2.size() - 1; i >= 0; i--) {
+            (*value1)[i] += value2[i];
+        }
+    }
+}
+
+template <>
+std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
+    std::stringstream s;
+    s << "{";
+    bool first = true;
+    for (uint64_t n : v) {
+        if (!first) {
+            s << ", ";
+        }
+        s << n;
+        first = false;
+    }
+    s << "}";
+    return s.str();
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/ui/Size.cpp b/libs/battery/LongArrayMultiStateCounter.h
similarity index 63%
copy from libs/ui/Size.cpp
copy to libs/battery/LongArrayMultiStateCounter.h
index d2996d1..f3439f6 100644
--- a/libs/ui/Size.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.h
@@ -1,5 +1,6 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +15,15 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+#pragma once
 
-namespace android::ui {
+#include <vector>
+#include "MultiStateCounter.h"
 
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+namespace android {
+namespace battery {
 
-} // namespace android::ui
+typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter;
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
new file mode 100644
index 0000000..e4e6b2a
--- /dev/null
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include "LongArrayMultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+class LongArrayMultiStateCounterTest : public testing::Test {};
+
+TEST_F(LongArrayMultiStateCounterTest, stateChange) {
+    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+    testCounter.setState(0, 1000);
+    testCounter.setState(1, 2000);
+    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+    // Time was split in half between the two states, so the counts will be split 50:50 too
+    EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0));
+    EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1));
+}
+
+TEST_F(LongArrayMultiStateCounterTest, accumulation) {
+    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+    testCounter.setState(0, 1000);
+    testCounter.setState(1, 2000);
+    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+    testCounter.setState(0, 4000);
+    testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000);
+
+    // The first delta is split 50:50:
+    //   0: {50, 100, 150, 200}
+    //   1: {50, 100, 150, 200}
+    // The second delta is split 4:1
+    //   0: {80, 80, 80, 80}
+    //   1: {20, 20, 20, 20}
+    EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0));
+    EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
+}
+
+TEST_F(LongArrayMultiStateCounterTest, toString) {
+    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+    testCounter.setState(0, 1000);
+    testCounter.setState(1, 2000);
+    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+    EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
+                 testCounter.toString().c_str());
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
new file mode 100644
index 0000000..0caf005
--- /dev/null
+++ b/libs/battery/MultiStateCounter.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <time.h>
+#include <sstream>
+#include <string>
+
+/**
+ * An object that can track changes of some value over time, taking into account an additional
+ * dimension: the object's state.  As the tracked value changes, the deltas are distributed
+ * among the object states in accordance with the time spent in those states.
+ */
+namespace android {
+namespace battery {
+
+typedef uint16_t state_t;
+
+template <class T>
+class MultiStateCounter {
+    uint16_t stateCount;
+    state_t currentState;
+    time_t lastStateChangeTimestamp;
+    T emptyValue;
+    T lastValue;
+    time_t lastUpdateTimestamp;
+    T deltaValue;
+    bool isEnabled;
+
+    struct State {
+        time_t timeInStateSinceUpdate;
+        T counter;
+    };
+
+    State* states;
+
+public:
+    MultiStateCounter(uint16_t stateCount, const T& emptyValue);
+
+    virtual ~MultiStateCounter();
+
+    void setEnabled(bool enabled, time_t timestamp);
+
+    void setState(state_t state, time_t timestamp);
+
+    void setValue(state_t state, const T& value);
+
+    /**
+     * Updates the value by distributing the delta from the previously set value
+     * among states according to their respective time-in-state.
+     * Returns the delta from the previously set value.
+     */
+    const T& updateValue(const T& value, time_t timestamp);
+
+    /**
+     * Updates the value by distributing the specified increment among states according
+     * to their respective time-in-state.
+     */
+    void incrementValue(const T& increment, time_t timestamp);
+
+    /**
+     * Adds the specified increment to the value for the current state, without affecting
+     * the last updated value or timestamp.  Ignores partial time-in-state: the entirety of
+     * the increment is given to the current state.
+     */
+    void addValue(const T& increment);
+
+    void reset();
+
+    uint16_t getStateCount();
+
+    const T& getCount(state_t state);
+
+    std::string toString();
+
+private:
+    /**
+     * Subtracts previousValue from newValue and returns the result in outValue.
+     * Returns true iff the combination of previousValue and newValue is valid
+     * (newValue >= prevValue)
+     */
+    bool delta(const T& previousValue, const T& newValue, T* outValue) const;
+
+    /**
+     * Adds value2 to value1 and stores the result in value1.  Denominator is
+     * guaranteed to be non-zero.
+     */
+    void add(T* value1, const T& value2, const uint64_t numerator,
+             const uint64_t denominator) const;
+
+    std::string valueToString(const T& value) const;
+};
+
+// ---------------------- MultiStateCounter Implementation -------------------------
+// Since MultiStateCounter is a template, the implementation must be inlined.
+
+template <class T>
+MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
+      : stateCount(stateCount),
+        currentState(0),
+        lastStateChangeTimestamp(-1),
+        emptyValue(emptyValue),
+        lastValue(emptyValue),
+        lastUpdateTimestamp(-1),
+        deltaValue(emptyValue),
+        isEnabled(true) {
+    states = new State[stateCount];
+    for (int i = 0; i < stateCount; i++) {
+        states[i].timeInStateSinceUpdate = 0;
+        states[i].counter = emptyValue;
+    }
+}
+
+template <class T>
+MultiStateCounter<T>::~MultiStateCounter() {
+    delete[] states;
+};
+
+template <class T>
+void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+    if (enabled == isEnabled) {
+        return;
+    }
+
+    if (!enabled) {
+        // Confirm the current state for the side-effect of updating the time-in-state
+        // counter for the current state.
+        setState(currentState, timestamp);
+    }
+
+    isEnabled = enabled;
+
+    if (lastStateChangeTimestamp >= 0) {
+        lastStateChangeTimestamp = timestamp;
+    }
+}
+
+template <class T>
+void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
+    if (isEnabled && lastStateChangeTimestamp >= 0) {
+        if (timestamp >= lastStateChangeTimestamp) {
+            states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
+        } else {
+            ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
+                  (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
+            // The accumulated durations have become unreliable. For example, if the timestamp
+            // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
+            // we would get 4000, which is greater than (last - first). This could lead to
+            // counts exceeding 100%.
+            for (int i = 0; i < stateCount; i++) {
+                states[i].timeInStateSinceUpdate = 0;
+            }
+        }
+    }
+    currentState = state;
+    lastStateChangeTimestamp = timestamp;
+}
+
+template <class T>
+void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+    states[state].counter = value;
+}
+
+template <class T>
+const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
+    T* returnValue = &emptyValue;
+
+    // If the counter is disabled, we ignore the update, except when the counter got disabled after
+    // the previous update, in which case we still need to pick up the residual delta.
+    if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
+        // Confirm the current state for the side-effect of updating the time-in-state
+        // counter for the current state.
+        setState(currentState, timestamp);
+
+        if (lastUpdateTimestamp >= 0) {
+            if (timestamp > lastUpdateTimestamp) {
+                if (delta(lastValue, value, &deltaValue)) {
+                    returnValue = &deltaValue;
+                    time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
+                    for (int i = 0; i < stateCount; i++) {
+                        time_t timeInState = states[i].timeInStateSinceUpdate;
+                        if (timeInState) {
+                            add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
+                            states[i].timeInStateSinceUpdate = 0;
+                        }
+                    }
+                } else {
+                    std::stringstream str;
+                    str << "updateValue is called with a value " << valueToString(value)
+                        << ", which is lower than the previous value " << valueToString(lastValue)
+                        << "\n";
+                    ALOGE("%s", str.str().c_str());
+
+                    for (int i = 0; i < stateCount; i++) {
+                        states[i].timeInStateSinceUpdate = 0;
+                    }
+                }
+            } else if (timestamp < lastUpdateTimestamp) {
+                ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
+                      (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
+
+                for (int i = 0; i < stateCount; i++) {
+                    states[i].timeInStateSinceUpdate = 0;
+                }
+            }
+        }
+    }
+    lastValue = value;
+    lastUpdateTimestamp = timestamp;
+    return *returnValue;
+}
+
+template <class T>
+void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
+    T newValue = lastValue;
+    add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
+    updateValue(newValue, timestamp);
+}
+
+template <class T>
+void MultiStateCounter<T>::addValue(const T& value) {
+    if (!isEnabled) {
+        return;
+    }
+    add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
+}
+
+template <class T>
+void MultiStateCounter<T>::reset() {
+    lastStateChangeTimestamp = -1;
+    lastUpdateTimestamp = -1;
+    for (int i = 0; i < stateCount; i++) {
+        states[i].timeInStateSinceUpdate = 0;
+        states[i].counter = emptyValue;
+    }
+}
+
+template <class T>
+uint16_t MultiStateCounter<T>::getStateCount() {
+    return stateCount;
+}
+
+template <class T>
+const T& MultiStateCounter<T>::getCount(state_t state) {
+    return states[state].counter;
+}
+
+template <class T>
+std::string MultiStateCounter<T>::toString() {
+    std::stringstream str;
+    str << "[";
+    for (int i = 0; i < stateCount; i++) {
+        if (i != 0) {
+            str << ", ";
+        }
+        str << i << ": " << valueToString(states[i].counter);
+        if (states[i].timeInStateSinceUpdate > 0) {
+            str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
+        }
+    }
+    str << "]";
+    if (lastUpdateTimestamp >= 0) {
+        str << " updated: " << lastUpdateTimestamp;
+    }
+    if (lastStateChangeTimestamp >= 0) {
+        str << " currentState: " << currentState;
+        if (lastStateChangeTimestamp > lastUpdateTimestamp) {
+            str << " stateChanged: " << lastStateChangeTimestamp;
+        }
+    } else {
+        str << " currentState: none";
+    }
+    if (!isEnabled) {
+        str << " disabled";
+    }
+    return str.str();
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
new file mode 100644
index 0000000..cb11a54
--- /dev/null
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include "MultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+typedef MultiStateCounter<double> DoubleMultiStateCounter;
+
+template <>
+bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
+                                    double* outValue) const {
+    *outValue = newValue - previousValue;
+    return *outValue >= 0;
+}
+
+template <>
+void DoubleMultiStateCounter::add(double* value1, const double& value2, const uint64_t numerator,
+                                  const uint64_t denominator) const {
+    if (numerator != denominator) {
+        // The caller ensures that denominator != 0
+        *value1 += value2 * numerator / denominator;
+    } else {
+        *value1 += value2;
+    }
+}
+
+template <>
+std::string DoubleMultiStateCounter::valueToString(const double& v) const {
+    return std::to_string(v);
+}
+
+class MultiStateCounterTest : public testing::Test {};
+
+TEST_F(MultiStateCounterTest, constructor) {
+    DoubleMultiStateCounter testCounter(3, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(1, 0);
+    double delta = testCounter.updateValue(3.14, 3000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+    EXPECT_DOUBLE_EQ(3.14, delta);
+}
+
+TEST_F(MultiStateCounterTest, stateChange) {
+    DoubleMultiStateCounter testCounter(3, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(1, 0);
+    testCounter.setState(2, 1000);
+    testCounter.updateValue(6.0, 3000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, setEnabled) {
+    DoubleMultiStateCounter testCounter(3, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(1, 0);
+    testCounter.setEnabled(false, 1000);
+    testCounter.setState(2, 2000);
+    testCounter.updateValue(6.0, 3000);
+
+    // In state 1: accumulated 1000 before disabled, that's 6.0 * 1000/3000 = 2.0
+    // In state 2: 0, since it is still disabled
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+    // Should have no effect since the counter is disabled
+    testCounter.setState(0, 3500);
+
+    // Should have no effect since the counter is disabled
+    testCounter.updateValue(10.0, 4000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+    testCounter.setState(2, 4500);
+
+    // Enable the counter to partially accumulate deltas for the current state, 2
+    testCounter.setEnabled(true, 5000);
+    testCounter.setEnabled(false, 6000);
+    testCounter.setEnabled(true, 7000);
+    testCounter.updateValue(20.0, 8000);
+
+    // The delta is 10.0 over 5000-3000=2000.
+    // Counter has been enabled in state 2 for (6000-5000)+(8000-7000) = 2000,
+    // so its share is (20.0-10.0) * 2000/(8000-4000) = 5.0
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(5.0, testCounter.getCount(2));
+
+    testCounter.reset();
+    testCounter.setState(0, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(1, 2000);
+    testCounter.setEnabled(false, 3000);
+    testCounter.updateValue(200, 5000);
+
+    // 200 over 5000 = 40 per second
+    // Counter was in state 0 from 0 to 2000, so 2 sec, so the count should be 40 * 2 = 80
+    // It stayed in state 1 from 2000 to 3000, at which point the counter was disabled,
+    // so the count for state 1 should be 40 * 1 = 40.
+    // The remaining 2 seconds from 3000 to 5000 don't count because the counter was disabled.
+    EXPECT_DOUBLE_EQ(80.0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(40.0, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, reset) {
+    DoubleMultiStateCounter testCounter(3, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(1, 0);
+    testCounter.updateValue(2.72, 3000);
+
+    testCounter.reset();
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+    // Assert that we can still continue accumulating after a reset
+    testCounter.updateValue(0, 4000);
+    testCounter.updateValue(3.14, 5000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_setState) {
+    DoubleMultiStateCounter testCounter(3, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(1, 0);
+    testCounter.setState(2, 2000);
+
+    // Time moves back
+    testCounter.setState(1, 1000);
+    testCounter.updateValue(6.0, 3000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+
+    // We were in state 1 from 0 to 2000, which was erased because the time moved back.
+    // Then from 1000 to 3000, so we expect the count to be 6 * (2000/3000)
+    EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(1));
+
+    // No time was effectively accumulated for state 2, because the timestamp moved back
+    // while we were in state 2.
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) {
+    DoubleMultiStateCounter testCounter(1, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(0, 0);
+    testCounter.updateValue(6.0, 2000);
+
+    // Time moves back. The delta over the negative interval from 2000 to 1000 is ignored
+    testCounter.updateValue(8.0, 1000);
+    double delta = testCounter.updateValue(11.0, 3000);
+
+    // The total accumulated count is:
+    //  6.0          // For the period 0-2000
+    //  +(11.0-8.0)  // For the period 1000-3000
+    EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+    //  11.0-8.0
+    EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, updateValue_nonmonotonic) {
+    DoubleMultiStateCounter testCounter(2, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(0, 0);
+    testCounter.updateValue(6.0, 2000);
+
+    // Value goes down. The negative delta from 6.0 to 4.0 is ignored
+    testCounter.updateValue(4.0, 3000);
+
+    // Value goes up again. The positive delta from 4.0 to 7.0 is accumulated.
+    double delta = testCounter.updateValue(7.0, 4000);
+
+    // The total accumulated count is:
+    //  6.0          // For the period 0-2000
+    //  +(7.0-4.0)   // For the period 3000-4000
+    EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+    //  7.0-4.0
+    EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, incrementValue) {
+    DoubleMultiStateCounter testCounter(2, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(0, 0);
+    testCounter.updateValue(6.0, 2000);
+
+    testCounter.setState(1, 3000);
+
+    testCounter.incrementValue(8.0, 6000);
+
+    // The total accumulated count is:
+    //  6.0             // For the period 0-2000
+    //  +(8.0 * 0.25)   // For the period 3000-4000
+    EXPECT_DOUBLE_EQ(8.0, testCounter.getCount(0));
+
+    // 0                // For the period 0-3000
+    // +(8.0 * 0.75)    // For the period 3000-4000
+    EXPECT_DOUBLE_EQ(6.0, testCounter.getCount(1));
+}
+
+TEST_F(MultiStateCounterTest, addValue) {
+    DoubleMultiStateCounter testCounter(1, 0);
+    testCounter.updateValue(0, 0);
+    testCounter.setState(0, 0);
+    testCounter.updateValue(6.0, 2000);
+
+    testCounter.addValue(8.0);
+
+    EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+
+    testCounter.setEnabled(false, 3000);
+    testCounter.addValue(888.0);
+
+    EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+}
+
+TEST_F(MultiStateCounterTest, toString) {
+    DoubleMultiStateCounter testCounter(2, 0);
+
+    EXPECT_STREQ("[0: 0.000000, 1: 0.000000] currentState: none", testCounter.toString().c_str());
+
+    testCounter.updateValue(0, 0);
+    testCounter.setState(1, 0);
+    testCounter.setState(1, 2000);
+    EXPECT_STREQ("[0: 0.000000, 1: 0.000000 timeInStateSinceUpdate: 2000]"
+                 " updated: 0 currentState: 1 stateChanged: 2000",
+                 testCounter.toString().c_str());
+
+    testCounter.updateValue(3.14, 3000);
+
+    EXPECT_STREQ("[0: 0.000000, 1: 3.140000] updated: 3000 currentState: 1",
+                 testCounter.toString().c_str());
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7d14315..f5abb85 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -228,10 +228,8 @@
   "android.gfx.tests.IIPCTest",
   "android.gfx.tests.ISafeInterfaceTest",
   "android.graphicsenv.IGpuService",
-  "android.gui.DisplayEventConnection",
   "android.gui.IConsumerListener",
   "android.gui.IGraphicBufferConsumer",
-  "android.gui.IRegionSamplingListener",
   "android.gui.ITransactionComposerListener",
   "android.gui.SensorEventConnection",
   "android.gui.SensorServer",
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 565542b..4163897 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -289,7 +289,7 @@
 /**
  * Built-in transaction for all binder objects. This sends a transaction that will immediately
  * return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a
- * sanity check.
+ * consistency check.
  *
  * Available since API level 29.
  *
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 2524c5f..5a80ad0 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -15,11 +15,13 @@
     },
     srcs: [
         "Flags_test.cpp",
+        "cast_test.cpp",
+        "enum_test.cpp",
         "future_test.cpp",
-        "NamedEnum_test.cpp",
         "small_map_test.cpp",
         "small_vector_test.cpp",
         "static_vector_test.cpp",
+        "string_test.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/libs/ftl/Flags_test.cpp b/libs/ftl/Flags_test.cpp
index 8c00b52..d241fa2 100644
--- a/libs/ftl/Flags_test.cpp
+++ b/libs/ftl/Flags_test.cpp
@@ -23,7 +23,7 @@
 
 using namespace android::flag_operators;
 
-enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+enum class TestFlags : uint8_t { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
 
 TEST(Flags, Test) {
     Flags<TestFlags> flags = TestFlags::ONE;
@@ -165,7 +165,7 @@
 
 TEST(Flags, String_UnknownValues) {
     auto flags = Flags<TestFlags>(0b1011);
-    ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+    ASSERT_EQ(flags.string(), "ONE | TWO | 0b1000");
 }
 
 TEST(FlagsIterator, IteratesOverAllFlags) {
@@ -210,18 +210,4 @@
     ASSERT_EQ(++iter, flags.end());
 }
 
-TEST(FlagNames, RuntimeFlagName) {
-    TestFlags f = TestFlags::ONE;
-    ASSERT_EQ(flag_name(f), "ONE");
-}
-
-TEST(FlagNames, RuntimeUnknownFlagName) {
-    TestFlags f = static_cast<TestFlags>(0x8);
-    ASSERT_EQ(flag_name(f), std::nullopt);
-}
-
-TEST(FlagNames, CompileTimeFlagName) {
-    static_assert(flag_name<TestFlags::TWO>() == "TWO");
-}
-
-} // namespace android::test
\ No newline at end of file
+} // namespace android::test
diff --git a/libs/ftl/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp
deleted file mode 100644
index dff2b8a..0000000
--- a/libs/ftl/NamedEnum_test.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <ftl/NamedEnum.h>
-
-namespace android {
-
-// Test enum class maximum enum value smaller than default maximum of 8.
-enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
-// Big enum contains enum values greater than default maximum of 8.
-enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };
-
-// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
-template <>
-constexpr size_t NamedEnum::max<TestBigEnums> = 16;
-
-namespace test {
-using android::TestBigEnums;
-using android::TestEnums;
-
-TEST(NamedEnum, RuntimeNamedEnum) {
-    TestEnums e = TestEnums::ZERO;
-    ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
-    e = TestEnums::ONE;
-    ASSERT_EQ(NamedEnum::enum_name(e), "ONE");
-
-    e = TestEnums::THREE;
-    ASSERT_EQ(NamedEnum::enum_name(e), "THREE");
-
-    e = TestEnums::SEVEN;
-    ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
-}
-
-// Test big enum
-TEST(NamedEnum, RuntimeBigNamedEnum) {
-    TestBigEnums e = TestBigEnums::ZERO;
-    ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
-    e = TestBigEnums::FIFTEEN;
-    ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeNamedEnumAsString) {
-    TestEnums e = TestEnums::ZERO;
-    ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
-    e = TestEnums::ONE;
-    ASSERT_EQ(NamedEnum::string(e), "ONE");
-
-    e = TestEnums::THREE;
-    ASSERT_EQ(NamedEnum::string(e), "THREE");
-
-    e = TestEnums::SEVEN;
-    ASSERT_EQ(NamedEnum::string(e), "SEVEN");
-}
-
-TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
-    TestBigEnums e = TestBigEnums::ZERO;
-    ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
-    e = TestBigEnums::FIFTEEN;
-    ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnum) {
-    TestEnums e = static_cast<TestEnums>(0x5);
-    ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
-    e = static_cast<TestEnums>(0x9);
-    ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
-    TestEnums e = static_cast<TestEnums>(0x5);
-    ASSERT_EQ(NamedEnum::string(e), "05");
-    e = static_cast<TestEnums>(0x9);
-    ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009");
-}
-
-TEST(NamedEnum, CompileTimeFlagName) {
-    static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
-    static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
-}
-
-} // namespace test
-
-} // namespace android
diff --git a/libs/ftl/cast_test.cpp b/libs/ftl/cast_test.cpp
new file mode 100644
index 0000000..2abcb8f
--- /dev/null
+++ b/libs/ftl/cast_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/cast.h>
+#include <gtest/gtest.h>
+
+#include <cfloat>
+#include <cmath>
+#include <limits>
+
+namespace android::test {
+
+using ftl::cast_safety;
+using ftl::CastSafety;
+
+template <typename T>
+constexpr T min = std::numeric_limits<T>::lowest();
+
+template <typename T>
+constexpr T max = std::numeric_limits<T>::max();
+
+template <typename T>
+constexpr T inf = std::numeric_limits<T>::infinity();
+
+template <typename T>
+constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
+
+// Keep in sync with example usage in header file.
+
+static_assert(cast_safety<uint8_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(128u) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(static_cast<float>(INT32_MAX)) == CastSafety::kOverflow);
+
+static_assert(cast_safety<float>(-DBL_MAX) == CastSafety::kUnderflow);
+
+// Unsigned to unsigned.
+
+static_assert(cast_safety<uint8_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(max<uint64_t>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>) + 1) ==
+              CastSafety::kOverflow);
+
+// Unsigned to signed.
+
+static_assert(cast_safety<int16_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint16_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) + 1) ==
+              CastSafety::kOverflow);
+
+// Signed to unsigned.
+
+static_assert(cast_safety<uint16_t>(0) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int16_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) + 1) ==
+              CastSafety::kOverflow);
+
+// Signed to signed.
+
+static_assert(cast_safety<int8_t>(-129) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(-128) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(127) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(128) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int16_t>(min<int32_t>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+// Float to float.
+
+static_assert(cast_safety<double>(max<float>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(min<float>) == CastSafety::kSafe);
+
+static_assert(cast_safety<float>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<float>(max<double>) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToFloat) {
+  EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(min<float>), min<double>)),
+            CastSafety::kUnderflow);
+  EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(max<float>), max<double>)),
+            CastSafety::kOverflow);
+}
+
+// Unsigned to float.
+
+static_assert(cast_safety<float>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<uint64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<uint64_t>) == CastSafety::kSafe);
+
+// Signed to float.
+
+static_assert(cast_safety<float>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<int64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<int64_t>) == CastSafety::kSafe);
+
+// Float to unsigned.
+
+static_assert(cast_safety<uint32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint16_t>(-inf<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(inf<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(NaN<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<uint32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<uint32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint64_t>(max<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(-.1) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<uint64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<uint64_t>)) == CastSafety::kOverflow);
+
+// Float to signed.
+
+static_assert(cast_safety<int32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int16_t>(-inf<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(inf<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(NaN<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<float>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int32_t>(static_cast<double>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int64_t>(max<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<float>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(static_cast<double>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToSigned) {
+  constexpr int32_t kMax = ftl::details::safe_limits<int32_t, float>::max();
+  static_assert(kMax == 2'147'483'520);
+  EXPECT_EQ(kMax, static_cast<int32_t>(std::nexttowardf(max<int32_t>, 0)));
+
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, 0)), CastSafety::kSafe);
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, 0)), CastSafety::kSafe);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, 0)), CastSafety::kSafe);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, 0)), CastSafety::kSafe);
+
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, min<float>)),
+            CastSafety::kUnderflow);
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, max<float>)),
+            CastSafety::kOverflow);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, min<double>)),
+            CastSafety::kUnderflow);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, max<double>)),
+            CastSafety::kOverflow);
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp
new file mode 100644
index 0000000..1fd43ab
--- /dev/null
+++ b/libs/ftl/enum_test.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+namespace {
+
+enum class E { A, B, C, F = 5, ftl_last = F };
+
+static_assert(ftl::enum_begin_v<E> == E::A);
+static_assert(ftl::enum_last_v<E> == E::F);
+static_assert(ftl::enum_size_v<E> == 6);
+
+static_assert(ftl::enum_name<E::B>() == "B");
+static_assert(ftl::enum_name<E::ftl_last>() == "F");
+static_assert(ftl::enum_name(E::C).value_or("?") == "C");
+static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+
+enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+
+static_assert(ftl::enum_begin_v<F> == F{0});
+static_assert(ftl::enum_last_v<F> == F{15});
+static_assert(ftl::enum_size_v<F> == 16);
+
+static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
+static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
+
+// If a scoped enum is unsigned, its implicit range corresponds to its bit indices.
+enum class Flags : std::uint8_t {
+  kNone = 0,
+  kFlag1 = 0b0000'0010,
+  kFlag4 = 0b0001'0000,
+  kFlag7 = 0b1000'0000,
+  kMask = kFlag1 | kFlag4 | kFlag7,
+  kAll = 0b1111'1111
+};
+
+static_assert(ftl::enum_begin_v<Flags> == Flags{0});
+static_assert(ftl::enum_last_v<Flags> == Flags{7});
+static_assert(ftl::enum_size_v<Flags> == 8);
+
+static_assert(ftl::enum_name<Flags::kNone>() == "kNone");
+static_assert(ftl::enum_name<Flags::kFlag4>() == "kFlag4");
+static_assert(ftl::enum_name<Flags::kFlag7>() == "kFlag7");
+
+// Though not flags, the enumerators are within the implicit range of bit indices.
+enum class Planet : std::uint8_t {
+  kMercury,
+  kVenus,
+  kEarth,
+  kMars,
+  kJupiter,
+  kSaturn,
+  kUranus,
+  kNeptune
+};
+
+constexpr Planet kPluto{ftl::enum_cast(Planet::kNeptune) + 1};  // Honorable mention.
+
+static_assert(ftl::enum_begin_v<Planet> == Planet::kMercury);
+static_assert(ftl::enum_last_v<Planet> == Planet::kNeptune);
+static_assert(ftl::enum_size_v<Planet> == 8);
+
+static_assert(ftl::enum_name<Planet::kMercury>() == "kMercury");
+static_assert(ftl::enum_name<Planet::kSaturn>() == "kSaturn");
+
+// Unscoped enum must define explicit range, even if the underlying type is fixed.
+enum Temperature : int {
+  kRoom = 20,
+  kFridge = 4,
+  kFreezer = -18,
+
+  ftl_first = kFreezer,
+  ftl_last = kRoom
+};
+
+static_assert(ftl::enum_begin_v<Temperature> == kFreezer);
+static_assert(ftl::enum_last_v<Temperature> == kRoom);
+static_assert(ftl::enum_size_v<Temperature> == 39);
+
+static_assert(ftl::enum_name<kFreezer>() == "kFreezer");
+static_assert(ftl::enum_name<kFridge>() == "kFridge");
+static_assert(ftl::enum_name<kRoom>() == "kRoom");
+
+}  // namespace
+
+TEST(Enum, Range) {
+  std::string string;
+  for (E v : ftl::enum_range<E>()) {
+    string += ftl::enum_name(v).value_or("?");
+  }
+  EXPECT_EQ(string, "ABC??F");
+}
+
+TEST(Enum, Name) {
+  {
+    EXPECT_EQ(ftl::flag_name(Flags::kFlag1), "kFlag1");
+    EXPECT_EQ(ftl::flag_name(Flags::kFlag7), "kFlag7");
+
+    EXPECT_EQ(ftl::flag_name(Flags::kNone), std::nullopt);
+    EXPECT_EQ(ftl::flag_name(Flags::kMask), std::nullopt);
+    EXPECT_EQ(ftl::flag_name(Flags::kAll), std::nullopt);
+  }
+  {
+    EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth");
+    EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune");
+
+    EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt);
+  }
+  {
+    EXPECT_EQ(ftl::enum_name(kRoom), "kRoom");
+    EXPECT_EQ(ftl::enum_name(kFridge), "kFridge");
+    EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer");
+
+    EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(-30)), std::nullopt);
+    EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(0)), std::nullopt);
+    EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(100)), std::nullopt);
+  }
+}
+
+TEST(Enum, String) {
+  {
+    EXPECT_EQ(ftl::flag_string(Flags::kFlag1), "kFlag1");
+    EXPECT_EQ(ftl::flag_string(Flags::kFlag7), "kFlag7");
+
+    EXPECT_EQ(ftl::flag_string(Flags::kNone), "0b0");
+    EXPECT_EQ(ftl::flag_string(Flags::kMask), "0b10010010");
+    EXPECT_EQ(ftl::flag_string(Flags::kAll), "0b11111111");
+  }
+  {
+    EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth");
+    EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune");
+
+    EXPECT_EQ(ftl::enum_string(kPluto), "8");
+  }
+  {
+    EXPECT_EQ(ftl::enum_string(kRoom), "kRoom");
+    EXPECT_EQ(ftl::enum_string(kFridge), "kFridge");
+    EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer");
+
+    EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(-30)), "-30");
+    EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(0)), "0");
+    EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(100)), "100");
+  }
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
index 323b9f9..ee650e5 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -18,6 +18,9 @@
 #include <gtest/gtest.h>
 
 #include <cctype>
+#include <string>
+
+using namespace std::string_literals;
 
 namespace android::test {
 
@@ -35,16 +38,19 @@
 
   EXPECT_TRUE(map.contains(123));
 
-  EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+  EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u);
 
-  const auto opt = map.find(-1);
+  const auto opt = map.get(-1);
   ASSERT_TRUE(opt);
 
   std::string& ref = *opt;
   EXPECT_TRUE(ref.empty());
   ref = "xyz";
 
-  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+  map.emplace_or_replace(0, "vanilla", 2u, 3u);
+  EXPECT_TRUE(map.dynamic());
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
 }
 
 TEST(SmallMap, Construct) {
@@ -90,42 +96,290 @@
   }
 }
 
+TEST(SmallMap, UniqueKeys) {
+  {
+    // Duplicate mappings are discarded.
+    const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1);
+
+    EXPECT_EQ(map.size(), 3u);
+    EXPECT_EQ(map.max_size(), 9u);
+
+    using Map = decltype(map);
+    EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f)));
+  }
+  {
+    // Duplicate mappings may be reordered.
+    const SmallMap map = ftl::init::map('a', 'A')(
+        'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F');
+
+    EXPECT_EQ(map.size(), 6u);
+    EXPECT_EQ(map.max_size(), 12u);
+
+    using Map = decltype(map);
+    EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F')));
+  }
+}
+
 TEST(SmallMap, Find) {
   {
     // Constant reference.
-    const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+    const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
 
-    const auto opt = map.find('b');
+    const auto opt = map.get('b');
     EXPECT_EQ(opt, 'B');
 
     const char d = 'D';
-    const auto ref = map.find('d').value_or(std::cref(d));
+    const auto ref = map.get('d').value_or(std::cref(d));
     EXPECT_EQ(ref.get(), 'D');
   }
   {
     // Mutable reference.
-    ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+    SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
 
-    const auto opt = map.find('c');
+    const auto opt = map.get('c');
     EXPECT_EQ(opt, 'C');
 
     char d = 'd';
-    const auto ref = map.find('d').value_or(std::ref(d));
+    const auto ref = map.get('d').value_or(std::ref(d));
     ref.get() = 'D';
     EXPECT_EQ(d, 'D');
   }
   {
     // Constant unary operation.
-    const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-    EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+    const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+    EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z');
   }
   {
     // Mutable unary operation.
-    ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-    EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+    SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+    EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); }));
 
     EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
   }
 }
 
+TEST(SmallMap, TryEmplace) {
+  SmallMap<int, std::string, 3> map;
+  using Pair = decltype(map)::value_type;
+
+  {
+    const auto [it, ok] = map.try_emplace(123, "abc");
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(123, "abc"s));
+  }
+  {
+    const auto [it, ok] = map.try_emplace(42, 3u, '?');
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(42, "???"s));
+  }
+  {
+    const auto [it, ok] = map.try_emplace(-1);
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(-1, std::string()));
+    EXPECT_FALSE(map.dynamic());
+  }
+  {
+    // Insertion fails if mapping exists.
+    const auto [it, ok] = map.try_emplace(42, "!!!");
+    EXPECT_FALSE(ok);
+    EXPECT_EQ(*it, Pair(42, "???"));
+    EXPECT_FALSE(map.dynamic());
+  }
+  {
+    // Insertion at capacity promotes the map.
+    const auto [it, ok] = map.try_emplace(999, "xyz");
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(999, "xyz"));
+    EXPECT_TRUE(map.dynamic());
+  }
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s)));
+}
+
+namespace {
+
+// The mapped type does not require a copy/move assignment operator.
+struct String {
+  template <typename... Args>
+  String(Args... args) : str(args...) {}
+  const std::string str;
+
+  bool operator==(const String& other) const { return other.str == str; }
+};
+
+}  // namespace
+
+TEST(SmallMap, TryReplace) {
+  SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+  using Pair = decltype(map)::value_type;
+
+  {
+    // Replacing fails unless mapping exists.
+    const auto it = map.try_replace(3, "c");
+    EXPECT_EQ(it, map.end());
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from one character.
+    const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+    ASSERT_NE(it, map.end());
+    EXPECT_EQ(*it, Pair(2, "b"));
+  }
+
+  EXPECT_FALSE(map.dynamic());
+  EXPECT_TRUE(map.try_emplace(3, "abc").second);
+  EXPECT_TRUE(map.try_emplace(4, "d").second);
+  EXPECT_TRUE(map.dynamic());
+
+  {
+    // Replacing fails unless mapping exists.
+    const auto it = map.try_replace(5, "e");
+    EXPECT_EQ(it, map.end());
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(3);
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from substring.
+    const auto it = map.try_replace(3, ref->get().str, 2u, 1u);
+    ASSERT_NE(it, map.end());
+    EXPECT_EQ(*it, Pair(3, "c"));
+  }
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, EmplaceOrReplace) {
+  SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+  using Pair = decltype(map)::value_type;
+
+  {
+    // New mapping is emplaced.
+    const auto [it, emplace] = map.emplace_or_replace(3, "c");
+    EXPECT_TRUE(emplace);
+    EXPECT_EQ(*it, Pair(3, "c"));
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from one character.
+    const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+    EXPECT_FALSE(emplace);
+    EXPECT_EQ(*it, Pair(2, "b"));
+  }
+
+  EXPECT_FALSE(map.dynamic());
+  EXPECT_FALSE(map.emplace_or_replace(3, "abc").second);  // Replace.
+  EXPECT_TRUE(map.emplace_or_replace(4, "d").second);     // Emplace.
+  EXPECT_TRUE(map.dynamic());
+
+  {
+    // New mapping is emplaced.
+    const auto [it, emplace] = map.emplace_or_replace(5, "e");
+    EXPECT_TRUE(emplace);
+    EXPECT_EQ(*it, Pair(5, "e"));
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(3);
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from substring.
+    const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u);
+    EXPECT_FALSE(emplace);
+    EXPECT_EQ(*it, Pair(3, "c"));
+  }
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, Erase) {
+  {
+    SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4');
+    EXPECT_FALSE(map.dynamic());
+
+    EXPECT_FALSE(map.erase(0));  // Key not found.
+
+    EXPECT_TRUE(map.erase(2));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(1));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(4));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+    EXPECT_TRUE(map.erase(3));
+    EXPECT_FALSE(map.erase(3));  // Key not found.
+
+    EXPECT_TRUE(map.empty());
+    EXPECT_FALSE(map.dynamic());
+  }
+  {
+    SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+    map.try_emplace(4, '4');
+    EXPECT_TRUE(map.dynamic());
+
+    EXPECT_FALSE(map.erase(0));  // Key not found.
+
+    EXPECT_TRUE(map.erase(2));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(1));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(4));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+    EXPECT_TRUE(map.erase(3));
+    EXPECT_FALSE(map.erase(3));  // Key not found.
+
+    EXPECT_TRUE(map.empty());
+    EXPECT_TRUE(map.dynamic());
+  }
+}
+
+TEST(SmallMap, Clear) {
+  SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+
+  map.clear();
+
+  EXPECT_TRUE(map.empty());
+  EXPECT_FALSE(map.dynamic());
+
+  map = ftl::init::map(1, '1')(2, '2')(3, '3');
+  map.try_emplace(4, '4');
+
+  map.clear();
+
+  EXPECT_TRUE(map.empty());
+  EXPECT_TRUE(map.dynamic());
+}
+
+TEST(SmallMap, KeyEqual) {
+  struct KeyEqual {
+    bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; }
+  };
+
+  SmallMap<int, char, 1, KeyEqual> map;
+
+  EXPECT_TRUE(map.try_emplace(3, '3').second);
+  EXPECT_FALSE(map.try_emplace(13, '3').second);
+
+  EXPECT_TRUE(map.try_emplace(22, '2').second);
+  EXPECT_TRUE(map.contains(42));
+
+  EXPECT_TRUE(map.try_emplace(111, '1').second);
+  EXPECT_EQ(map.get(321), '1');
+
+  map.erase(123);
+  EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2')));
+}
+
 }  // namespace android::test
diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp
index 3a03e69..4237496 100644
--- a/libs/ftl/small_vector_test.cpp
+++ b/libs/ftl/small_vector_test.cpp
@@ -460,4 +460,34 @@
   EXPECT_EQ(0, dead);
 }
 
+TEST(SmallVector, Clear) {
+  int live = 0;
+  int dead = 0;
+
+  SmallVector<DestroyCounts, 2> counts;
+  counts.emplace_back(live, dead);
+  counts.emplace_back(live, dead);
+
+  counts.clear();
+
+  EXPECT_TRUE(counts.empty());
+  EXPECT_FALSE(counts.dynamic());
+
+  EXPECT_EQ(2, live);
+  EXPECT_EQ(0, dead);
+
+  live = 0;
+  counts.emplace_back(live, dead);
+  counts.emplace_back(live, dead);
+  counts.emplace_back(live, dead);
+
+  counts.clear();
+
+  EXPECT_TRUE(counts.empty());
+  EXPECT_TRUE(counts.dynamic());
+
+  EXPECT_EQ(3, live);
+  EXPECT_EQ(2, dead);
+}
+
 }  // namespace android::test
diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp
index cbe8dff..2de3ad2 100644
--- a/libs/ftl/static_vector_test.cpp
+++ b/libs/ftl/static_vector_test.cpp
@@ -396,4 +396,19 @@
   EXPECT_EQ(0, dead);
 }
 
+TEST(StaticVector, Clear) {
+  int live = 0;
+  int dead = 0;
+
+  StaticVector<DestroyCounts, 5> counts;
+  counts.emplace_back(live, dead);
+  counts.emplace_back(live, dead);
+
+  counts.clear();
+
+  EXPECT_TRUE(counts.empty());
+  EXPECT_EQ(2, live);
+  EXPECT_EQ(0, dead);
+}
+
 }  // namespace android::test
diff --git a/libs/ftl/string_test.cpp b/libs/ftl/string_test.cpp
new file mode 100644
index 0000000..f3d85c8
--- /dev/null
+++ b/libs/ftl/string_test.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/string.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <limits>
+#include <sstream>
+#include <type_traits>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(String, ToChars) {
+  ftl::to_chars_buffer_t<> buffer;
+
+  EXPECT_EQ(ftl::to_chars(buffer, 123u), "123");
+  EXPECT_EQ(ftl::to_chars(buffer, -42, ftl::Radix::kBin), "-0b101010");
+  EXPECT_EQ(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex), "0xcafe");
+  EXPECT_EQ(ftl::to_chars(buffer, '*', ftl::Radix::kHex), "0x2a");
+}
+
+namespace {
+
+template <typename F, typename T>
+void ToCharsTest() {
+  constexpr auto kRadix = F::kRadix;
+
+  using Limits = std::numeric_limits<T>;
+  constexpr auto kMin = Limits::min();
+  constexpr auto kMax = Limits::max();
+  constexpr auto kNeg = static_cast<T>(-42);
+  constexpr auto kPos = static_cast<T>(123);
+
+  ftl::to_chars_buffer_t<T> buffer;
+
+  EXPECT_EQ(ftl::to_chars(buffer, kMin, kRadix), F{}(kMin));
+  EXPECT_EQ(ftl::to_chars(buffer, kMax, kRadix), F{}(kMax));
+  EXPECT_EQ(ftl::to_chars(buffer, kNeg, kRadix), F{}(kNeg));
+  EXPECT_EQ(ftl::to_chars(buffer, kPos, kRadix), F{}(kPos));
+}
+
+template <typename...>
+struct Types {};
+
+template <typename F, typename Types>
+struct ToCharsTests;
+
+template <typename F, typename T, typename... Ts>
+struct ToCharsTests<F, Types<T, Ts...>> {
+  static void test() {
+    ToCharsTest<F, T>();
+    ToCharsTests<F, Types<Ts...>>::test();
+  }
+};
+
+template <typename F>
+struct ToCharsTests<F, Types<>> {
+  static void test() {}
+};
+
+template <typename T, typename U = std::make_unsigned_t<T>>
+U to_unsigned(std::ostream& stream, T v) {
+  if (std::is_same_v<T, U>) return v;
+
+  if (v < 0) {
+    stream << '-';
+    return std::numeric_limits<U>::max() - static_cast<U>(v) + 1;
+  } else {
+    return static_cast<U>(v);
+  }
+}
+
+struct Bin {
+  static constexpr auto kRadix = ftl::Radix::kBin;
+
+  template <typename T>
+  std::string operator()(T v) const {
+    std::ostringstream stream;
+    auto u = to_unsigned(stream, v);
+    stream << "0b";
+
+    if (u == 0) {
+      stream << 0;
+    } else {
+      std::ostringstream digits;
+      do {
+        digits << (u & 1);
+      } while (u >>= 1);
+
+      const auto str = digits.str();
+      std::copy(str.rbegin(), str.rend(), std::ostream_iterator<char>(stream));
+    }
+
+    return stream.str();
+  }
+};
+
+struct Dec {
+  static constexpr auto kRadix = ftl::Radix::kDec;
+
+  template <typename T>
+  std::string operator()(T v) const {
+    return std::to_string(v);
+  }
+};
+
+struct Hex {
+  static constexpr auto kRadix = ftl::Radix::kHex;
+
+  template <typename T>
+  std::string operator()(T v) const {
+    std::ostringstream stream;
+    const auto u = to_unsigned(stream, v);
+    stream << "0x" << std::hex << std::nouppercase;
+    stream << (sizeof(T) == 1 ? static_cast<unsigned>(u) : u);
+    return stream.str();
+  }
+};
+
+using IntegerTypes =
+    Types<char, unsigned char, signed char, std::uint8_t, std::uint16_t, std::uint32_t,
+          std::uint64_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t>;
+
+}  // namespace
+
+TEST(String, ToCharsBin) {
+  ToCharsTests<Bin, IntegerTypes>::test();
+
+  {
+    const std::uint8_t x = 0b1111'1111;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "0b11111111");
+  }
+  {
+    const std::int16_t x = -0b1000'0000'0000'0000;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "-0b1000000000000000");
+  }
+}
+
+TEST(String, ToCharsDec) {
+  ToCharsTests<Dec, IntegerTypes>::test();
+
+  {
+    const std::uint32_t x = UINT32_MAX;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x), "4294967295");
+  }
+  {
+    const std::int32_t x = INT32_MIN;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x), "-2147483648");
+  }
+}
+
+TEST(String, ToCharsHex) {
+  ToCharsTests<Hex, IntegerTypes>::test();
+
+  {
+    const std::uint16_t x = 0xfade;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kHex), "0xfade");
+  }
+  {
+    ftl::to_chars_buffer_t<> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, INT64_MIN, ftl::Radix::kHex), "-0x8000000000000000");
+  }
+}
+
+}  // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cda9e19..6afd172 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -33,7 +33,7 @@
     target: {
         darwin: {
             enabled: false,
-        }
+        },
     },
 
     vendor_available: true,
@@ -48,18 +48,18 @@
     min_sdk_version: "29",
 
     srcs: [
-        "Gralloc4.cpp"
+        "Gralloc4.cpp",
     ],
 
     shared_libs: [
-        "android.hardware.graphics.common-V2-ndk",
+        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
         "liblog",
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.graphics.common-V2-ndk",
+        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
     ],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index e2f072a..61e6657 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -1134,6 +1134,18 @@
                           decodeByteVector);
 }
 
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+                            hidl_vec<uint8_t>* outSmpte2094_10) {
+    return encodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+                                  encodeByteVector);
+}
+
+status_t decodeSmpte2094_10(const hidl_vec<uint8_t>& smpte2094_10,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+    return decodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+                                  decodeByteVector);
+}
+
 status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
                       hidl_vec<uint8_t>* output) {
     return encodeMetadata(metadataType, input, output, encodeInteger);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 2f418ac..deaffad 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -134,6 +134,12 @@
                                              aidl::android::hardware::graphics::common::
                                                      StandardMetadataType::SMPTE2094_40)};
 
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2094_10 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                     static_cast<int64_t>(
+                                             aidl::android::hardware::graphics::common::
+                                                     StandardMetadataType::SMPTE2094_10)};
+
 /*---------------------------------------------------------------------------------------------*/
 
 /**
@@ -327,6 +333,11 @@
 status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
                             std::optional<std::vector<uint8_t>>* outSmpte2094_40);
 
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+                            android::hardware::hidl_vec<uint8_t>* outSmpte2094_10);
+status_t decodeSmpte2094_10(const android::hardware::hidl_vec<uint8_t>& smpte2094_10,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_10);
+
 /**
  * The functions below can be used to encode and decode vendor metadata types.
  */
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
index 89cbf4a..94e344f 100644
--- a/libs/gralloc/types/tests/Gralloc4_test.cpp
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -455,6 +455,32 @@
     ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
 }
 
+class Gralloc4TestSmpte2094_10
+      : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> {};
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestSmpte2094_10Params, Gralloc4TestSmpte2094_10,
+        ::testing::Values(
+                std::optional<std::vector<uint8_t>>({}),
+                std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+                std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+                                                     std::numeric_limits<uint8_t>::min() + 1,
+                                                     std::numeric_limits<uint8_t>::min() + 2,
+                                                     std::numeric_limits<uint8_t>::min() + 3,
+                                                     std::numeric_limits<uint8_t>::min() + 4}),
+                std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+                                                     std::numeric_limits<uint8_t>::max() - 1,
+                                                     std::numeric_limits<uint8_t>::max() - 2,
+                                                     std::numeric_limits<uint8_t>::max() - 3,
+                                                     std::numeric_limits<uint8_t>::max() - 4}),
+                std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_10, Smpte2094_10) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(),
+                                                             gralloc4::encodeSmpte2094_10,
+                                                             gralloc4::decodeSmpte2094_10));
+}
+
 class Gralloc4TestBufferDescriptorInfo : public testing::TestWithParam<BufferDescriptorInfo> { };
 
 INSTANTIATE_TEST_CASE_P(
@@ -491,6 +517,7 @@
     ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_10({{}}, nullptr));
 }
 
 TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
@@ -516,6 +543,7 @@
     ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_10(vec, nullptr));
 }
 
 TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index bd9abc1..ec3587b 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,7 +55,7 @@
 filegroup {
     name: "guiconstants_aidl",
     srcs: [
-        "android/**/DropInputMode.aidl",
+        "android/gui/DropInputMode.aidl",
         "android/**/TouchOcclusionMode.aidl",
     ],
 }
@@ -66,11 +66,14 @@
     host_supported: true,
     srcs: [
         ":guiconstants_aidl",
+        ":inputconstants_aidl",
+        "android/gui/DisplayInfo.aidl",
         "android/gui/FocusRequest.aidl",
         "android/gui/InputApplicationInfo.aidl",
         "android/gui/IWindowInfosListener.aidl",
         "android/gui/IWindowInfosReportedListener.aidl",
         "android/gui/WindowInfo.aidl",
+        "DisplayInfo.cpp",
         "WindowInfo.cpp",
     ],
 
@@ -91,7 +94,7 @@
     ],
 
     aidl: {
-        export_aidl_headers: true
+        export_aidl_headers: true,
     },
 
     include_dirs: [
@@ -178,11 +181,9 @@
         "FrameTimelineInfo.cpp",
         "GLConsumer.cpp",
         "IConsumerListener.cpp",
-        "IDisplayEventConnection.cpp",
         "IGraphicBufferConsumer.cpp",
         "IGraphicBufferProducer.cpp",
         "IProducerListener.cpp",
-        "IRegionSamplingListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
         "ITransactionCompletedListener.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index f034642..dd96683 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,7 +132,7 @@
         mSize(1, 1),
         mRequestedSize(mSize),
         mFormat(PIXEL_FORMAT_RGBA_8888),
-        mNextTransaction(nullptr) {
+        mSyncTransaction(nullptr) {
     createBufferQueue(&mProducer, &mConsumer);
     // since the adapter is in the client process, set dequeue timeout
     // explicitly so that dequeueBuffer will block
@@ -155,6 +155,7 @@
 
     ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
     mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
+    mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
     BQA_LOGV("BLASTBufferQueue created");
@@ -173,14 +174,14 @@
     BQA_LOGE("Applying pending transactions on dtor %d",
              static_cast<uint32_t>(mPendingTransactions.size()));
     SurfaceComposerClient::Transaction t;
-    for (auto& [targetFrameNumber, transaction] : mPendingTransactions) {
-        t.merge(std::move(transaction));
-    }
-    t.apply();
+    mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
+    t.setApplyToken(mApplyToken).apply();
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
                               int32_t format, SurfaceComposerClient::Transaction* outTransaction) {
+    LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
+
     std::unique_lock _lock{mMutex};
     if (mFormat != format) {
         mFormat = format;
@@ -188,21 +189,22 @@
     }
 
     SurfaceComposerClient::Transaction t;
-    const bool setBackpressureFlag = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
+    const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
+    if (surfaceControlChanged && mSurfaceControl != nullptr) {
+        BQA_LOGD("Updating SurfaceControl without recreating BBQ");
+    }
     bool applyTransaction = false;
 
     // Always update the native object even though they might have the same layer handle, so we can
     // get the updated transform hint from WM.
     mSurfaceControl = surface;
-    if (mSurfaceControl != nullptr) {
-        if (setBackpressureFlag) {
-            t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
-                       layer_state_t::eEnableBackpressure);
-            applyTransaction = true;
-        }
-        mTransformHint = mSurfaceControl->getTransformHint();
-        mBufferItemConsumer->setTransformHint(mTransformHint);
+    if (surfaceControlChanged) {
+        t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
+                   layer_state_t::eEnableBackpressure);
+        applyTransaction = true;
     }
+    mTransformHint = mSurfaceControl->getTransformHint();
+    mBufferItemConsumer->setTransformHint(mTransformHint);
     BQA_LOGV("update width=%d height=%d format=%d mTransformHint=%d", width, height, format,
              mTransformHint);
 
@@ -216,11 +218,9 @@
             mSize = mRequestedSize;
             SurfaceComposerClient::Transaction* destFrameTransaction =
                     (outTransaction) ? outTransaction : &t;
-            if (mSurfaceControl != nullptr) {
-                destFrameTransaction->setDestinationFrame(mSurfaceControl,
-                                                          Rect(0, 0, newSize.getWidth(),
-                                                               newSize.getHeight()));
-            }
+            destFrameTransaction->setDestinationFrame(mSurfaceControl,
+                                                      Rect(0, 0, newSize.getWidth(),
+                                                           newSize.getHeight()));
             applyTransaction = true;
         }
     }
@@ -273,10 +273,10 @@
                 // case, we don't actually want to flush the frames in between since they will get
                 // processed and merged with the sync transaction and released earlier than if they
                 // were sent to SF
-                if (mWaitForTransactionCallback && mNextTransaction == nullptr &&
+                if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
                     currFrameNumber >= mLastAcquiredFrameNumber) {
                     mWaitForTransactionCallback = false;
-                    flushShadowQueueLocked();
+                    flushShadowQueue();
                 }
             } else {
                 BQA_LOGE("Failed to find matching SurfaceControl in transactionCommittedCallback");
@@ -302,9 +302,6 @@
 
 void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
                                            const std::vector<SurfaceControlStats>& stats) {
-    std::function<void(int64_t)> transactionCompleteCallback = nullptr;
-    uint64_t currFrameNumber = 0;
-
     {
         std::unique_lock _lock{mMutex};
         ATRACE_CALL();
@@ -331,28 +328,6 @@
                                                     stat.latchTime,
                                                     stat.frameEventStats.dequeueReadyTime);
                 }
-                currFrameNumber = stat.frameEventStats.frameNumber;
-
-                if (mTransactionCompleteCallback &&
-                    currFrameNumber >= mTransactionCompleteFrameNumber) {
-                    if (currFrameNumber > mTransactionCompleteFrameNumber) {
-                        BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
-                                 " than expected=%" PRIu64,
-                                 currFrameNumber, mTransactionCompleteFrameNumber);
-                    }
-                    transactionCompleteCallback = std::move(mTransactionCompleteCallback);
-                    mTransactionCompleteFrameNumber = 0;
-                }
-                std::vector<ReleaseCallbackId> staleReleases;
-                for (const auto& [key, value]: mSubmitted) {
-                    if (currFrameNumber > key.framenumber) {
-                        staleReleases.push_back(key);
-                    }
-                }
-                for (const auto& staleRelease : staleReleases) {
-                    releaseBufferCallbackLocked(staleRelease, stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE,
-                                                stat.transformHint, stat.currentMaxAcquiredBufferCount);
-                }
             } else {
                 BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
             }
@@ -361,13 +336,8 @@
                      "empty.");
         }
 
-
         decStrong((void*)transactionCallbackThunk);
     }
-
-    if (transactionCompleteCallback) {
-        transactionCompleteCallback(currFrameNumber);
-    }
 }
 
 // Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
@@ -375,19 +345,18 @@
 // So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
 // Otherwise, this is a no-op.
 static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
-                                       const sp<Fence>& releaseFence, uint32_t transformHint,
-                                       uint32_t currentMaxAcquiredBufferCount) {
+                                       const sp<Fence>& releaseFence,
+                                       std::optional<uint32_t> currentMaxAcquiredBufferCount) {
     sp<BLASTBufferQueue> blastBufferQueue = context.promote();
     if (blastBufferQueue) {
-        blastBufferQueue->releaseBufferCallback(id, releaseFence, transformHint,
-                                                currentMaxAcquiredBufferCount);
+        blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
     } else {
         ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
     }
 }
 
-void BLASTBufferQueue::flushShadowQueueLocked() {
-    BQA_LOGV("flushShadowQueueLocked");
+void BLASTBufferQueue::flushShadowQueue() {
+    BQA_LOGV("flushShadowQueue");
     int numFramesToFlush = mNumFrameAvailable;
     while (numFramesToFlush > 0) {
         acquireNextBufferLocked(std::nullopt);
@@ -395,31 +364,13 @@
     }
 }
 
-void BLASTBufferQueue::flushShadowQueue() {
-    std::unique_lock _lock{mMutex};
-    flushShadowQueueLocked();
-}
-
-void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
-                                             const sp<Fence>& releaseFence, uint32_t transformHint,
-                                             uint32_t currentMaxAcquiredBufferCount) {
-    std::unique_lock _lock{mMutex};
-    releaseBufferCallbackLocked(id, releaseFence, transformHint, currentMaxAcquiredBufferCount);
-}
-
-void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id,
-                                                   const sp<Fence>& releaseFence, uint32_t transformHint,
-                                                   uint32_t currentMaxAcquiredBufferCount) {
+void BLASTBufferQueue::releaseBufferCallback(
+        const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+        std::optional<uint32_t> currentMaxAcquiredBufferCount) {
     ATRACE_CALL();
+    std::unique_lock _lock{mMutex};
     BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
 
-    if (mSurfaceControl != nullptr) {
-        mTransformHint = transformHint;
-        mSurfaceControl->setTransformHint(transformHint);
-        mBufferItemConsumer->setTransformHint(mTransformHint);
-        BQA_LOGV("updated mTransformHint=%d", mTransformHint);
-    }
-
     // Calculate how many buffers we need to hold before we release them back
     // to the buffer queue. This will prevent higher latency when we are running
     // on a lower refresh rate than the max supported. We only do that for EGL
@@ -429,29 +380,21 @@
         return it != mSubmitted.end() && it->second.mApi == NATIVE_WINDOW_API_EGL;
     }();
 
-    const auto numPendingBuffersToHold =
-            isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0;
-    auto rb = ReleasedBuffer{id, releaseFence};
-    if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
-        mPendingRelease.emplace_back(rb);
+    if (currentMaxAcquiredBufferCount) {
+        mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount;
     }
 
+    const auto numPendingBuffersToHold =
+            isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
+    mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
+
     // Release all buffers that are beyond the ones that we need to hold
     while (mPendingRelease.size() > numPendingBuffersToHold) {
-        const auto releaseBuffer = mPendingRelease.front();
+        const auto releasedBuffer = mPendingRelease.front();
         mPendingRelease.pop_front();
-        auto it = mSubmitted.find(releaseBuffer.callbackId);
-        if (it == mSubmitted.end()) {
-            BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
-                     releaseBuffer.callbackId.to_string().c_str());
-            return;
-        }
-        mNumAcquired--;
-        BQA_LOGV("released %s", id.to_string().c_str());
-        mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
-        mSubmitted.erase(it);
+        releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
         // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
-        // onFrameAvailable handle processing them since it will merge with the nextTransaction.
+        // onFrameAvailable handle processing them since it will merge with the syncTransaction.
         if (!mWaitForTransactionCallback) {
             acquireNextBufferLocked(std::nullopt);
         }
@@ -463,6 +406,20 @@
     mCallbackCV.notify_all();
 }
 
+void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
+                                     const sp<Fence>& releaseFence) {
+    auto it = mSubmitted.find(callbackId);
+    if (it == mSubmitted.end()) {
+        BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
+                 callbackId.to_string().c_str());
+        return;
+    }
+    mNumAcquired--;
+    BQA_LOGV("released %s", callbackId.to_string().c_str());
+    mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+    mSubmitted.erase(it);
+}
+
 void BLASTBufferQueue::acquireNextBufferLocked(
         const std::optional<SurfaceComposerClient::Transaction*> transaction) {
     ATRACE_CALL();
@@ -543,14 +500,12 @@
 
     auto releaseBufferCallback =
             std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
-                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
-                      std::placeholders::_4);
-    t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback);
+                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+    sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+    t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseBufferCallback);
     t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
     t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
     t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
-    t->setAcquireFence(mSurfaceControl,
-                       bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
 
     mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
@@ -564,7 +519,6 @@
     if (!bufferItem.mIsAutoTimestamp) {
         t->setDesiredPresentTime(bufferItem.mTimestamp);
     }
-    t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
 
     if (!mNextFrameTimelineInfoQueue.empty()) {
         t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
@@ -586,21 +540,7 @@
         }
     }
 
-    auto mergeTransaction =
-            [&t, currentFrameNumber = bufferItem.mFrameNumber](
-                    std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
-                auto& [targetFrameNumber, transaction] = pendingTransaction;
-                if (currentFrameNumber < targetFrameNumber) {
-                    return false;
-                }
-                t->merge(std::move(transaction));
-                return true;
-            };
-
-    mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
-                                              mPendingTransactions.end(), mergeTransaction),
-                               mPendingTransactions.end());
-
+    mergePendingTransactions(t, bufferItem.mFrameNumber);
     if (applyTransaction) {
         t->setApplyToken(mApplyToken).apply();
     }
@@ -634,58 +574,83 @@
     mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
 }
 
+void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
+    if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
+        // We are waiting on a previous sync's transaction callback so allow another sync
+        // transaction to proceed.
+        //
+        // We need to first flush out the transactions that were in between the two syncs.
+        // We do this by merging them into mSyncTransaction so any buffer merging will get
+        // a release callback invoked. The release callback will be async so we need to wait
+        // on max acquired to make sure we have the capacity to acquire another buffer.
+        if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+            BQA_LOGD("waiting to flush shadow queue...");
+            mCallbackCV.wait(lock);
+        }
+        while (mNumFrameAvailable > 0) {
+            // flush out the shadow queue
+            acquireAndReleaseBuffer();
+        }
+    }
+
+    while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+        BQA_LOGD("waiting for free buffer.");
+        mCallbackCV.wait(lock);
+    }
+}
+
 void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
     ATRACE_CALL();
     std::unique_lock _lock{mMutex};
 
-    const bool nextTransactionSet = mNextTransaction != nullptr;
-    BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet));
-    if (nextTransactionSet) {
-        if (mWaitForTransactionCallback) {
-            // We are waiting on a previous sync's transaction callback so allow another sync
-            // transaction to proceed.
-            //
-            // We need to first flush out the transactions that were in between the two syncs.
-            // We do this by merging them into mNextTransaction so any buffer merging will get
-            // a release callback invoked. The release callback will be async so we need to wait
-            // on max acquired to make sure we have the capacity to acquire another buffer.
-            if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
-                BQA_LOGD("waiting to flush shadow queue...");
-                mCallbackCV.wait(_lock);
-            }
-            while (mNumFrameAvailable > 0) {
-                // flush out the shadow queue
-                acquireAndReleaseBuffer();
+    const bool syncTransactionSet = mSyncTransaction != nullptr;
+    BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
+
+    if (syncTransactionSet) {
+        bool mayNeedToWaitForBuffer = true;
+        // If we are going to re-use the same mSyncTransaction, release the buffer that may already
+        // be set in the Transaction. This is to allow us a free slot early to continue processing
+        // a new buffer.
+        if (!mAcquireSingleBuffer) {
+            auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
+            if (bufferData) {
+                BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
+                         bufferData->frameNumber);
+                releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence);
+                // Because we just released a buffer, we know there's no need to wait for a free
+                // buffer.
+                mayNeedToWaitForBuffer = false;
             }
         }
 
-        while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
-            BQA_LOGD("waiting for free buffer.");
-            mCallbackCV.wait(_lock);
+        if (mayNeedToWaitForBuffer) {
+            flushAndWaitForFreeBuffer(_lock);
         }
     }
 
     // add to shadow queue
     mNumFrameAvailable++;
-    if (mWaitForTransactionCallback && mNumFrameAvailable == 2) {
+    if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
         acquireAndReleaseBuffer();
     }
     ATRACE_INT(mQueuedBufferTrace.c_str(),
                mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
 
-    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
-             boolToString(nextTransactionSet));
+    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber,
+             boolToString(syncTransactionSet));
 
-    if (nextTransactionSet) {
-        acquireNextBufferLocked(std::move(mNextTransaction));
+    if (syncTransactionSet) {
+        acquireNextBufferLocked(mSyncTransaction);
 
         // Only need a commit callback when syncing to ensure the buffer that's synced has been sent
         // to SF
         incStrong((void*)transactionCommittedCallbackThunk);
-        mNextTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
+        mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
                                                           static_cast<void*>(this));
 
-        mNextTransaction = nullptr;
+        if (mAcquireSingleBuffer) {
+            mSyncTransaction = nullptr;
+        }
         mWaitForTransactionCallback = true;
     } else if (!mWaitForTransactionCallback) {
         acquireNextBufferLocked(std::nullopt);
@@ -707,9 +672,11 @@
     mDequeueTimestamps.erase(bufferId);
 };
 
-void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
+void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t,
+                                          bool acquireSingleBuffer) {
     std::lock_guard _lock{mMutex};
-    mNextTransaction = t;
+    mSyncTransaction = t;
+    mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true;
 }
 
 bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
@@ -734,24 +701,13 @@
     return mSize != bufferSize;
 }
 
-void BLASTBufferQueue::setTransactionCompleteCallback(
-        uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
-    std::lock_guard _lock{mMutex};
-    if (transactionCompleteCallback == nullptr) {
-        mTransactionCompleteCallback = nullptr;
-    } else {
-        mTransactionCompleteCallback = std::move(transactionCompleteCallback);
-        mTransactionCompleteFrameNumber = frameNumber;
-    }
-}
-
 // Check if we have acquired the maximum number of buffers.
 // Consumer can acquire an additional buffer if that buffer is not droppable. Set
 // includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
 // of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
 bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
     int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1);
-    return mNumAcquired == maxAcquiredBuffers;
+    return mNumAcquired >= maxAcquiredBuffers;
 }
 
 class BBQSurface : public Surface {
@@ -852,6 +808,32 @@
     }
 }
 
+void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) {
+    std::lock_guard _lock{mMutex};
+
+    SurfaceComposerClient::Transaction t;
+    mergePendingTransactions(&t, frameNumber);
+    t.setApplyToken(mApplyToken).apply();
+}
+
+void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
+                                                uint64_t frameNumber) {
+    auto mergeTransaction =
+            [&t, currentFrameNumber = frameNumber](
+                    std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+                auto& [targetFrameNumber, transaction] = pendingTransaction;
+                if (currentFrameNumber < targetFrameNumber) {
+                    return false;
+                }
+                t->merge(std::move(transaction));
+                return true;
+            };
+
+    mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
+                                              mPendingTransactions.end(), mergeTransaction),
+                               mPendingTransactions.end());
+}
+
 // Maintains a single worker thread per process that services a list of runnables.
 class AsyncWorker : public Singleton<AsyncWorker> {
 private:
@@ -999,4 +981,53 @@
     return mLastAcquiredFrameNumber;
 }
 
+void BLASTBufferQueue::abandon() {
+    std::unique_lock _lock{mMutex};
+    // flush out the shadow queue
+    while (mNumFrameAvailable > 0) {
+        acquireAndReleaseBuffer();
+    }
+
+    // Clear submitted buffer states
+    mNumAcquired = 0;
+    mSubmitted.clear();
+    mPendingRelease.clear();
+
+    if (!mPendingTransactions.empty()) {
+        BQA_LOGD("Applying pending transactions on abandon %d",
+                 static_cast<uint32_t>(mPendingTransactions.size()));
+        SurfaceComposerClient::Transaction t;
+        mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
+        t.setApplyToken(mApplyToken).apply();
+    }
+
+    // Clear sync states
+    if (mWaitForTransactionCallback) {
+        BQA_LOGD("mWaitForTransactionCallback cleared");
+        mWaitForTransactionCallback = false;
+    }
+
+    if (mSyncTransaction != nullptr) {
+        BQA_LOGD("mSyncTransaction cleared mAcquireSingleBuffer=%s",
+                 mAcquireSingleBuffer ? "true" : "false");
+        mSyncTransaction = nullptr;
+        mAcquireSingleBuffer = false;
+    }
+
+    // abandon buffer queue
+    if (mBufferItemConsumer != nullptr) {
+        mBufferItemConsumer->abandon();
+        mBufferItemConsumer->setFrameAvailableListener(nullptr);
+        mBufferItemConsumer->setBufferFreedListener(nullptr);
+    }
+    mBufferItemConsumer = nullptr;
+    mConsumer = nullptr;
+    mProducer = nullptr;
+}
+
+bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const {
+    std::unique_lock _lock{mMutex};
+    return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
+}
+
 } // namespace android
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 46800f2..ee80082 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -146,6 +146,19 @@
     return 1; // keep the callback
 }
 
+void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event,
+                                                    VsyncEventData* outVsyncEventData) const {
+    for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+        DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline =
+                event.vsync.frameTimelines[i];
+        outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId,
+                                                .deadlineTimestamp =
+                                                        receiverTimeline.deadlineTimestamp,
+                                                .expectedPresentTime =
+                                                        receiverTimeline.expectedVSyncTimestamp};
+    }
+}
+
 bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
                                                   PhysicalDisplayId* outDisplayId,
                                                   uint32_t* outCount,
@@ -169,6 +182,9 @@
                     outVsyncEventData->id = ev.vsync.vsyncId;
                     outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
                     outVsyncEventData->frameInterval = ev.vsync.frameInterval;
+                    outVsyncEventData->preferredFrameTimelineIndex =
+                            ev.vsync.preferredFrameTimelineIndex;
+                    populateFrameTimelines(ev, outVsyncEventData);
                     break;
                 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                     dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 03b33c7..b916e48 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -19,7 +19,6 @@
 #include <utils/Errors.h>
 
 #include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
 #include <gui/ISurfaceComposer.h>
 
 #include <private/gui/ComposerService.h>
diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp
new file mode 100644
index 0000000..52d9540
--- /dev/null
+++ b/libs/gui/DisplayInfo.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DisplayInfo"
+
+#include <binder/Parcel.h>
+#include <gui/DisplayInfo.h>
+#include <private/gui/ParcelUtils.h>
+
+#include <log/log.h>
+
+namespace android::gui {
+
+// --- DisplayInfo ---
+
+status_t DisplayInfo::readFromParcel(const android::Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    float dsdx, dtdx, tx, dtdy, dsdy, ty;
+    SAFE_PARCEL(parcel->readInt32, &displayId);
+    SAFE_PARCEL(parcel->readInt32, &logicalWidth);
+    SAFE_PARCEL(parcel->readInt32, &logicalHeight);
+    SAFE_PARCEL(parcel->readFloat, &dsdx);
+    SAFE_PARCEL(parcel->readFloat, &dtdx);
+    SAFE_PARCEL(parcel->readFloat, &tx);
+    SAFE_PARCEL(parcel->readFloat, &dtdy);
+    SAFE_PARCEL(parcel->readFloat, &dsdy);
+    SAFE_PARCEL(parcel->readFloat, &ty);
+
+    transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+    return OK;
+}
+
+status_t DisplayInfo::writeToParcel(android::Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return BAD_VALUE;
+    }
+
+    SAFE_PARCEL(parcel->writeInt32, displayId);
+    SAFE_PARCEL(parcel->writeInt32, logicalWidth);
+    SAFE_PARCEL(parcel->writeInt32, logicalHeight);
+    SAFE_PARCEL(parcel->writeFloat, transform.dsdx());
+    SAFE_PARCEL(parcel->writeFloat, transform.dtdx());
+    SAFE_PARCEL(parcel->writeFloat, transform.tx());
+    SAFE_PARCEL(parcel->writeFloat, transform.dtdy());
+    SAFE_PARCEL(parcel->writeFloat, transform.dsdy());
+    SAFE_PARCEL(parcel->writeFloat, transform.ty());
+
+    return OK;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp
index 9231a57..3800b88 100644
--- a/libs/gui/FrameTimelineInfo.cpp
+++ b/libs/gui/FrameTimelineInfo.cpp
@@ -33,12 +33,14 @@
 status_t FrameTimelineInfo::write(Parcel& output) const {
     SAFE_PARCEL(output.writeInt64, vsyncId);
     SAFE_PARCEL(output.writeInt32, inputEventId);
+    SAFE_PARCEL(output.writeInt64, startTimeNanos);
     return NO_ERROR;
 }
 
 status_t FrameTimelineInfo::read(const Parcel& input) {
     SAFE_PARCEL(input.readInt64, &vsyncId);
     SAFE_PARCEL(input.readInt32, &inputEventId);
+    SAFE_PARCEL(input.readInt64, &startTimeNanos);
     return NO_ERROR;
 }
 
@@ -48,16 +50,19 @@
         if (other.vsyncId > vsyncId) {
             vsyncId = other.vsyncId;
             inputEventId = other.inputEventId;
+            startTimeNanos = other.startTimeNanos;
         }
     } else if (vsyncId == INVALID_VSYNC_ID) {
         vsyncId = other.vsyncId;
         inputEventId = other.inputEventId;
+        startTimeNanos = other.startTimeNanos;
     }
 }
 
 void FrameTimelineInfo::clear() {
     vsyncId = INVALID_VSYNC_ID;
     inputEventId = IInputConstants::INVALID_INPUT_EVENT_ID;
+    startTimeNanos = 0;
 }
 
 }; // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
deleted file mode 100644
index c0e246f..0000000
--- a/libs/gui/IDisplayEventConnection.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/IDisplayEventConnection.h>
-
-#include <private/gui/BitTube.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
-    STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
-    SET_VSYNC_RATE,
-    REQUEST_NEXT_VSYNC,
-    LAST = REQUEST_NEXT_VSYNC,
-};
-
-} // Anonymous namespace
-
-class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
-public:
-    explicit BpDisplayEventConnection(const sp<IBinder>& impl)
-          : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {}
-
-    ~BpDisplayEventConnection() override;
-
-    status_t stealReceiveChannel(gui::BitTube* outChannel) override {
-        return callRemote<decltype(
-                &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL,
-                                                                outChannel);
-    }
-
-    status_t setVsyncRate(uint32_t count) override {
-        return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE,
-                                                                            count);
-    }
-
-    void requestNextVsync() override {
-        callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
-                Tag::REQUEST_NEXT_VSYNC);
-    }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpDisplayEventConnection::~BpDisplayEventConnection() = default;
-
-IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
-
-status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                              uint32_t flags) {
-    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
-        return BBinder::onTransact(code, data, reply, flags);
-    }
-    auto tag = static_cast<Tag>(code);
-    switch (tag) {
-        case Tag::STEAL_RECEIVE_CHANNEL:
-            return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
-        case Tag::SET_VSYNC_RATE:
-            return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
-        case Tag::REQUEST_NEXT_VSYNC:
-            return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
-    }
-}
-
-} // namespace android
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
deleted file mode 100644
index 40cbfce..0000000
--- a/libs/gui/IRegionSamplingListener.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IRegionSamplingListener"
-//#define LOG_NDEBUG 0
-
-#include <gui/IRegionSamplingListener.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
-    ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
-    LAST = ON_SAMPLE_COLLECTED,
-};
-
-} // Anonymous namespace
-
-class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
-public:
-    explicit BpRegionSamplingListener(const sp<IBinder>& impl)
-          : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
-
-    ~BpRegionSamplingListener() override;
-
-    void onSampleCollected(float medianLuma) override {
-        callRemoteAsync<decltype(
-                &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
-    }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpRegionSamplingListener::~BpRegionSamplingListener() = default;
-
-IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
-
-status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                              uint32_t flags) {
-    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
-        return BBinder::onTransact(code, data, reply, flags);
-    }
-    auto tag = static_cast<Tag>(code);
-    switch (tag) {
-        case Tag::ON_SAMPLE_COLLECTED:
-            return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
-    }
-}
-
-} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 1726761..a931709 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,13 +17,13 @@
 // tag as surfaceflinger
 #define LOG_TAG "SurfaceFlinger"
 
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/IRegionSamplingListener.h>
 #include <android/gui/ITransactionTraceListener.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
-#include <gui/IDisplayEventConnection.h>
 #include <gui/IGraphicBufferProducer.h>
-#include <gui/IRegionSamplingListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerDebugInfo.h>
@@ -44,6 +44,8 @@
 
 namespace android {
 
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
 using gui::IWindowInfosListener;
 using ui::ColorMode;
 
@@ -124,11 +126,11 @@
         return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
     }
 
-    status_t captureDisplay(uint64_t displayOrLayerStack,
+    status_t captureDisplay(DisplayId displayId,
                             const sp<IScreenCaptureListener>& captureListener) override {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+        SAFE_PARCEL(data.writeUint64, displayId.value);
         SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
 
         return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
@@ -282,9 +284,14 @@
             NO_ERROR) {
             std::vector<uint64_t> rawIds;
             if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
-                std::vector<PhysicalDisplayId> displayIds(rawIds.size());
-                std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
-                               [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
+                std::vector<PhysicalDisplayId> displayIds;
+                displayIds.reserve(rawIds.size());
+
+                for (const uint64_t rawId : rawIds) {
+                    if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+                        displayIds.push_back(*id);
+                    }
+                }
                 return displayIds;
             }
         }
@@ -299,8 +306,11 @@
                     &reply);
         uint64_t rawId;
         SAFE_PARCEL(reply.readUint64, &rawId);
-        *displayId = PhysicalDisplayId(rawId);
-        return NO_ERROR;
+        if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+            *displayId = *id;
+            return NO_ERROR;
+        }
+        return NAME_NOT_FOUND;
     }
 
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override {
@@ -1124,6 +1134,34 @@
         return NO_ERROR;
     }
 
+    status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                         bool* outSupport) const override {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to write interface token: %d", error);
+            return error;
+        }
+        error = data.writeStrongBinder(displayToken);
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to write display token: %d", error);
+            return error;
+        }
+        error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_DECORATION_SUPPORT, data, &reply);
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to transact: %d", error);
+            return error;
+        }
+        bool support;
+        error = reply.readBool(&support);
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to read support: %d", error);
+            return error;
+        }
+        *outSupport = support;
+        return NO_ERROR;
+    }
+
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                           int8_t compatibility, int8_t changeFrameRateStrategy) override {
         Parcel data, reply;
@@ -1142,41 +1180,6 @@
         return reply.readInt32();
     }
 
-    status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override {
-        if (!outToken) return BAD_VALUE;
-
-        Parcel data, reply;
-        status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        if (err != NO_ERROR) {
-            ALOGE("acquireFrameRateFlexibilityToken: failed writing interface token: %s (%d)",
-                  strerror(-err), -err);
-            return err;
-        }
-
-        err = remote()->transact(BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, data,
-                                 &reply);
-        if (err != NO_ERROR) {
-            ALOGE("acquireFrameRateFlexibilityToken: failed to transact: %s (%d)", strerror(-err),
-                  err);
-            return err;
-        }
-
-        err = reply.readInt32();
-        if (err != NO_ERROR) {
-            ALOGE("acquireFrameRateFlexibilityToken: call failed: %s (%d)", strerror(-err), err);
-            return err;
-        }
-
-        err = reply.readStrongBinder(outToken);
-        if (err != NO_ERROR) {
-            ALOGE("acquireFrameRateFlexibilityToken: failed reading binder token: %s (%d)",
-                  strerror(-err), err);
-            return err;
-        }
-
-        return NO_ERROR;
-    }
-
     status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
                                   const FrameTimelineInfo& frameTimelineInfo) override {
         Parcel data, reply;
@@ -1355,12 +1358,15 @@
         }
         case CAPTURE_DISPLAY_BY_ID: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            uint64_t displayOrLayerStack = 0;
+            uint64_t value;
+            SAFE_PARCEL(data.readUint64, &value);
+            const auto id = DisplayId::fromValue(value);
+            if (!id) return BAD_VALUE;
+
             sp<IScreenCaptureListener> captureListener;
-            SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
             SAFE_PARCEL(data.readStrongBinder, &captureListener);
 
-            return captureDisplay(displayOrLayerStack, captureListener);
+            return captureDisplay(*id, captureListener);
         }
         case CAPTURE_LAYERS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1427,9 +1433,9 @@
         }
         case GET_PHYSICAL_DISPLAY_TOKEN: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            PhysicalDisplayId displayId(data.readUint64());
-            sp<IBinder> display = getPhysicalDisplayToken(displayId);
-            reply->writeStrongBinder(display);
+            const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64());
+            if (!id) return BAD_VALUE;
+            reply->writeStrongBinder(getPhysicalDisplayToken(*id));
             return NO_ERROR;
         }
         case GET_DISPLAY_STATE: {
@@ -2038,6 +2044,19 @@
             return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
                                            lightRadius);
         }
+        case GET_DISPLAY_DECORATION_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> displayToken;
+            status_t error = data.readNullableStrongBinder(&displayToken);
+            if (error != NO_ERROR) {
+                ALOGE("getDisplayDecorationSupport: failed to read display token: %d", error);
+                return error;
+            }
+            bool support = false;
+            error = getDisplayDecorationSupport(displayToken, &support);
+            reply->writeBool(support);
+            return error;
+        }
         case SET_FRAME_RATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> binder;
@@ -2062,16 +2081,6 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
-        case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> token;
-            status_t result = acquireFrameRateFlexibilityToken(&token);
-            reply->writeInt32(result);
-            if (result == NO_ERROR) {
-                reply->writeStrongBinder(token);
-            }
-            return NO_ERROR;
-        }
         case SET_FRAME_TIMELINE_INFO: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> binder;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 98e8b54..aa7ebc9 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -254,11 +254,10 @@
     }
 
     void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
-                         uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override {
+                         uint32_t currentMaxAcquiredBufferCount) override {
         callRemoteAsync<decltype(
                 &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
                                                                   callbackId, releaseFence,
-                                                                  transformHint,
                                                                   currentMaxAcquiredBufferCount);
     }
 };
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 77a883b..acd9ac5 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -41,7 +41,6 @@
         z(0),
         w(0),
         h(0),
-        layerStack(0),
         alpha(0),
         flags(0),
         mask(0),
@@ -51,7 +50,6 @@
         transform(0),
         transformToDisplayInverse(false),
         crop(Rect::INVALID_RECT),
-        orientedDisplaySpaceRect(Rect::INVALID_RECT),
         dataspace(ui::Dataspace::UNKNOWN),
         surfaceDamageRegion(),
         api(-1),
@@ -65,12 +63,10 @@
         frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
         changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
         fixedTransformHint(ui::Transform::ROT_INVALID),
-        frameNumber(0),
         autoRefresh(false),
         isTrustedOverlay(false),
         bufferCrop(Rect::INVALID_RECT),
         destinationFrame(Rect::INVALID_RECT),
-        releaseBufferListener(nullptr),
         dropInputMode(gui::DropInputMode::NONE) {
     matrix.dsdx = matrix.dtdy = 1.0f;
     matrix.dsdy = matrix.dtdx = 0.0f;
@@ -87,7 +83,7 @@
     SAFE_PARCEL(output.writeInt32, z);
     SAFE_PARCEL(output.writeUint32, w);
     SAFE_PARCEL(output.writeUint32, h);
-    SAFE_PARCEL(output.writeUint32, layerStack);
+    SAFE_PARCEL(output.writeUint32, layerStack.id);
     SAFE_PARCEL(output.writeFloat, alpha);
     SAFE_PARCEL(output.writeUint32, flags);
     SAFE_PARCEL(output.writeUint32, mask);
@@ -103,21 +99,6 @@
     SAFE_PARCEL(output.write, transparentRegion);
     SAFE_PARCEL(output.writeUint32, transform);
     SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
-    SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
-
-    if (buffer) {
-        SAFE_PARCEL(output.writeBool, true);
-        SAFE_PARCEL(output.write, *buffer);
-    } else {
-        SAFE_PARCEL(output.writeBool, false);
-    }
-
-    if (acquireFence) {
-        SAFE_PARCEL(output.writeBool, true);
-        SAFE_PARCEL(output.write, *acquireFence);
-    } else {
-        SAFE_PARCEL(output.writeBool, false);
-    }
 
     SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
     SAFE_PARCEL(output.write, hdrMetadata);
@@ -134,8 +115,6 @@
     SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
     SAFE_PARCEL(output.writeFloat, cornerRadius);
     SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
-    SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
-    SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
     SAFE_PARCEL(output.writeParcelable, metadata);
     SAFE_PARCEL(output.writeFloat, bgColorAlpha);
     SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
@@ -152,9 +131,7 @@
     SAFE_PARCEL(output.writeByte, frameRateCompatibility);
     SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
     SAFE_PARCEL(output.writeUint32, fixedTransformHint);
-    SAFE_PARCEL(output.writeUint64, frameNumber);
     SAFE_PARCEL(output.writeBool, autoRefresh);
-    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
 
     SAFE_PARCEL(output.writeUint32, blurRegions.size());
     for (auto region : blurRegions) {
@@ -175,8 +152,8 @@
     SAFE_PARCEL(output.write, destinationFrame);
     SAFE_PARCEL(output.writeBool, isTrustedOverlay);
 
-    SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
     SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
+    SAFE_PARCEL(bufferData.write, output);
     return NO_ERROR;
 }
 
@@ -190,7 +167,7 @@
     SAFE_PARCEL(input.readInt32, &z);
     SAFE_PARCEL(input.readUint32, &w);
     SAFE_PARCEL(input.readUint32, &h);
-    SAFE_PARCEL(input.readUint32, &layerStack);
+    SAFE_PARCEL(input.readUint32, &layerStack.id);
     SAFE_PARCEL(input.readFloat, &alpha);
 
     SAFE_PARCEL(input.readUint32, &flags);
@@ -216,20 +193,6 @@
     SAFE_PARCEL(input.read, transparentRegion);
     SAFE_PARCEL(input.readUint32, &transform);
     SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
-    SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
-
-    bool tmpBool = false;
-    SAFE_PARCEL(input.readBool, &tmpBool);
-    if (tmpBool) {
-        buffer = new GraphicBuffer();
-        SAFE_PARCEL(input.read, *buffer);
-    }
-
-    SAFE_PARCEL(input.readBool, &tmpBool);
-    if (tmpBool) {
-        acquireFence = new Fence();
-        SAFE_PARCEL(input.read, *acquireFence);
-    }
 
     uint32_t tmpUint32 = 0;
     SAFE_PARCEL(input.readUint32, &tmpUint32);
@@ -238,6 +201,8 @@
     SAFE_PARCEL(input.read, hdrMetadata);
     SAFE_PARCEL(input.read, surfaceDamageRegion);
     SAFE_PARCEL(input.readInt32, &api);
+
+    bool tmpBool = false;
     SAFE_PARCEL(input.readBool, &tmpBool);
     if (tmpBool) {
         sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
@@ -246,10 +211,6 @@
     SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
     SAFE_PARCEL(input.readFloat, &cornerRadius);
     SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
-    sp<IBinder> tmpBinder;
-    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
-    cachedBuffer.token = tmpBinder;
-    SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
     SAFE_PARCEL(input.readParcelable, &metadata);
 
     SAFE_PARCEL(input.readFloat, &bgColorAlpha);
@@ -274,15 +235,8 @@
     SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
     SAFE_PARCEL(input.readUint32, &tmpUint32);
     fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
-    SAFE_PARCEL(input.readUint64, &frameNumber);
     SAFE_PARCEL(input.readBool, &autoRefresh);
 
-    tmpBinder = nullptr;
-    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
-    if (tmpBinder) {
-        releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
-    }
-
     uint32_t numRegions = 0;
     SAFE_PARCEL(input.readUint32, &numRegions);
     blurRegions.clear();
@@ -306,11 +260,10 @@
     SAFE_PARCEL(input.read, destinationFrame);
     SAFE_PARCEL(input.readBool, &isTrustedOverlay);
 
-    SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
-
     uint32_t mode;
     SAFE_PARCEL(input.readUint32, &mode);
     dropInputMode = static_cast<gui::DropInputMode>(mode);
+    SAFE_PARCEL(bufferData.read, input);
     return NO_ERROR;
 }
 
@@ -322,21 +275,14 @@
     return state.read(input);
 }
 
-DisplayState::DisplayState()
-      : what(0),
-        layerStack(0),
-        flags(0),
-        layerStackSpaceRect(Rect::EMPTY_RECT),
-        orientedDisplaySpaceRect(Rect::EMPTY_RECT),
-        width(0),
-        height(0) {}
+DisplayState::DisplayState() = default;
 
 status_t DisplayState::write(Parcel& output) const {
     SAFE_PARCEL(output.writeStrongBinder, token);
     SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
     SAFE_PARCEL(output.writeUint32, what);
-    SAFE_PARCEL(output.writeUint32, layerStack);
     SAFE_PARCEL(output.writeUint32, flags);
+    SAFE_PARCEL(output.writeUint32, layerStack.id);
     SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
     SAFE_PARCEL(output.write, layerStackSpaceRect);
     SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
@@ -352,8 +298,8 @@
     surface = interface_cast<IGraphicBufferProducer>(tmpBinder);
 
     SAFE_PARCEL(input.readUint32, &what);
-    SAFE_PARCEL(input.readUint32, &layerStack);
     SAFE_PARCEL(input.readUint32, &flags);
+    SAFE_PARCEL(input.readUint32, &layerStack.id);
     uint32_t tmpUint = 0;
     SAFE_PARCEL(input.readUint32, &tmpUint);
     orientation = ui::toRotation(tmpUint);
@@ -468,12 +414,7 @@
     }
     if (other.what & eBufferChanged) {
         what |= eBufferChanged;
-        buffer = other.buffer;
-        releaseBufferEndpoint = other.releaseBufferEndpoint;
-    }
-    if (other.what & eAcquireFenceChanged) {
-        what |= eAcquireFenceChanged;
-        acquireFence = other.acquireFence;
+        bufferData = other.bufferData;
     }
     if (other.what & eDataspaceChanged) {
         what |= eDataspaceChanged;
@@ -506,11 +447,6 @@
         what |= eInputInfoChanged;
         windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle);
     }
-    if (other.what & eCachedBufferChanged) {
-        what |= eCachedBufferChanged;
-        cachedBuffer = other.cachedBuffer;
-        releaseBufferEndpoint = other.releaseBufferEndpoint;
-    }
     if (other.what & eBackgroundColorChanged) {
         what |= eBackgroundColorChanged;
         color = other.color;
@@ -539,10 +475,6 @@
         what |= eFixedTransformHintChanged;
         fixedTransformHint = other.fixedTransformHint;
     }
-    if (other.what & eFrameNumberChanged) {
-        what |= eFrameNumberChanged;
-        frameNumber = other.frameNumber;
-    }
     if (other.what & eAutoRefreshChanged) {
         what |= eAutoRefreshChanged;
         autoRefresh = other.autoRefresh;
@@ -551,13 +483,6 @@
         what |= eTrustedOverlayChanged;
         isTrustedOverlay = other.isTrustedOverlay;
     }
-    if (other.what & eReleaseBufferListenerChanged) {
-        if (releaseBufferListener) {
-            ALOGW("Overriding releaseBufferListener");
-        }
-        what |= eReleaseBufferListenerChanged;
-        releaseBufferListener = other.releaseBufferListener;
-    }
     if (other.what & eStretchChanged) {
         what |= eStretchChanged;
         stretchEffect = other.stretchEffect;
@@ -570,6 +495,9 @@
         what |= eDestinationFrameChanged;
         destinationFrame = other.destinationFrame;
     }
+    if (other.what & eProducerDisconnect) {
+        what |= eProducerDisconnect;
+    }
     if (other.what & eDropInputModeChanged) {
         what |= eDropInputModeChanged;
         dropInputMode = other.dropInputMode;
@@ -580,17 +508,17 @@
     }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
-              "other.what=0x%" PRIu64 " what=0x%" PRIu64,
-              other.what, what);
+              "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
+              other.what, what, (other.what & what) ^ other.what);
     }
 }
 
 bool layer_state_t::hasBufferChanges() const {
-    return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged);
+    return what & layer_state_t::eBufferChanged;
 }
 
 bool layer_state_t::hasValidBuffer() const {
-    return buffer || cachedBuffer.isValid();
+    return bufferData.buffer || bufferData.cachedBuffer.isValid();
 }
 
 status_t layer_state_t::matrix22_t::write(Parcel& output) const {
@@ -622,9 +550,7 @@
 }
 
 bool InputWindowCommands::empty() const {
-    bool empty = true;
-    empty = focusRequests.empty() && !syncInputWindows;
-    return empty;
+    return focusRequests.empty() && !syncInputWindows;
 }
 
 void InputWindowCommands::clear() {
@@ -751,4 +677,70 @@
     return NO_ERROR;
 }
 
+ReleaseCallbackId BufferData::generateReleaseCallbackId() const {
+    return {buffer->getId(), frameNumber};
+}
+
+status_t BufferData::write(Parcel& output) const {
+    SAFE_PARCEL(output.writeInt32, flags.get());
+
+    if (buffer) {
+        SAFE_PARCEL(output.writeBool, true);
+        SAFE_PARCEL(output.write, *buffer);
+    } else {
+        SAFE_PARCEL(output.writeBool, false);
+    }
+
+    if (acquireFence) {
+        SAFE_PARCEL(output.writeBool, true);
+        SAFE_PARCEL(output.write, *acquireFence);
+    } else {
+        SAFE_PARCEL(output.writeBool, false);
+    }
+
+    SAFE_PARCEL(output.writeUint64, frameNumber);
+    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
+    SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
+
+    SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
+    SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
+
+    return NO_ERROR;
+}
+
+status_t BufferData::read(const Parcel& input) {
+    int32_t tmpInt32;
+    SAFE_PARCEL(input.readInt32, &tmpInt32);
+    flags = Flags<BufferDataChange>(tmpInt32);
+
+    bool tmpBool = false;
+    SAFE_PARCEL(input.readBool, &tmpBool);
+    if (tmpBool) {
+        buffer = new GraphicBuffer();
+        SAFE_PARCEL(input.read, *buffer);
+    }
+
+    SAFE_PARCEL(input.readBool, &tmpBool);
+    if (tmpBool) {
+        acquireFence = new Fence();
+        SAFE_PARCEL(input.read, *acquireFence);
+    }
+
+    SAFE_PARCEL(input.readUint64, &frameNumber);
+
+    sp<IBinder> tmpBinder = nullptr;
+    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    if (tmpBinder) {
+        releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+    }
+    SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
+
+    tmpBinder = nullptr;
+    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    cachedBuffer.token = tmpBinder;
+    SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
+
+    return NO_ERROR;
+}
+
 }; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 353a91d..20c4146 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1846,9 +1846,10 @@
     ATRACE_CALL();
     auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
     auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
+    auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
 
     ALOGV("Surface::%s", __func__);
-    return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId});
+    return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos});
 }
 
 bool Surface::transformToDisplayInverse() const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 725ea65..b10c384 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -53,6 +53,7 @@
 namespace android {
 
 using gui::FocusRequest;
+using gui::IRegionSamplingListener;
 using gui::WindowInfo;
 using gui::WindowInfoHandle;
 using gui::WindowInfosListener;
@@ -61,6 +62,14 @@
 
 ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
 
+namespace {
+// Initialize transaction id counter used to generate transaction ids
+std::atomic<uint32_t> idCounter = 0;
+int64_t generateId() {
+    return (((int64_t)getpid()) << 32) | ++idCounter;
+}
+} // namespace
+
 ComposerService::ComposerService()
 : Singleton<ComposerService>() {
     Mutex::Autolock _l(mLock);
@@ -214,12 +223,6 @@
     mReleaseBufferCallbacks[callbackId] = listener;
 }
 
-void TransactionCompletedListener::removeReleaseBufferCallback(
-        const ReleaseCallbackId& callbackId) {
-    std::scoped_lock<std::mutex> lock(mMutex);
-    mReleaseBufferCallbacks.erase(callbackId);
-}
-
 void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
         sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
     std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
@@ -296,7 +299,7 @@
                                       transactionStats.latchTime, surfaceStats.acquireTime,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
-                                      surfaceStats.eventStats, surfaceStats.currentMaxAcquiredBufferCount);
+                                      surfaceStats.eventStats);
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -321,7 +324,7 @@
                                       transactionStats.latchTime, surfaceStats.acquireTime,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
-                                      surfaceStats.eventStats, surfaceStats.currentMaxAcquiredBufferCount);
+                                      surfaceStats.eventStats);
                 if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
                     callbacksMap[callbackId]
                             .surfaceControls[surfaceStats.surfaceControl]
@@ -343,7 +346,6 @@
                                  surfaceStats.previousReleaseFence
                                          ? surfaceStats.previousReleaseFence
                                          : Fence::NO_FENCE,
-                                 surfaceStats.transformHint,
                                  surfaceStats.currentMaxAcquiredBufferCount);
                     }
                 }
@@ -359,6 +361,10 @@
             // through all until the SC is found.
             int32_t layerId = -1;
             for (auto callbackId : transactionStats.callbackIds) {
+                if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+                    // We only want to run the stats callback for ON_COMPLETE
+                    continue;
+                }
                 sp<SurfaceControl> sc =
                         callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl];
                 if (sc != nullptr) {
@@ -367,7 +373,7 @@
                 }
             }
 
-            {
+            if (layerId != -1) {
                 // Acquire surface stats listener lock such that we guarantee that after calling
                 // unregister, there won't be any further callback.
                 std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
@@ -389,7 +395,7 @@
 }
 
 void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
-                                                   sp<Fence> releaseFence, uint32_t transformHint,
+                                                   sp<Fence> releaseFence,
                                                    uint32_t currentMaxAcquiredBufferCount) {
     ReleaseBufferCallback callback;
     {
@@ -401,7 +407,11 @@
               callbackId.to_string().c_str());
         return;
     }
-    callback(callbackId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
+    std::optional<uint32_t> optionalMaxAcquiredBufferCount =
+            currentMaxAcquiredBufferCount == UINT_MAX
+            ? std::nullopt
+            : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
+    callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
 }
 
 ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -416,6 +426,14 @@
     return callback;
 }
 
+void TransactionCompletedListener::removeReleaseBufferCallback(
+        const ReleaseCallbackId& callbackId) {
+    {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        popReleaseBufferCallbackLocked(callbackId);
+    }
+}
+
 // ---------------------------------------------------------------------------
 
 void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -525,10 +543,6 @@
 
 // ---------------------------------------------------------------------------
 
-// Initialize transaction id counter used to generate transaction ids
-// Transactions will start counting at 1, 0 is used for invalid transactions
-std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1;
-
 SurfaceComposerClient::Transaction::Transaction() {
     mId = generateId();
 }
@@ -560,9 +574,6 @@
     return nullptr;
 }
 
-int64_t SurfaceComposerClient::Transaction::generateId() {
-    return (((int64_t)getpid()) << 32) | idCounter++;
-}
 
 status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
     const uint32_t forceSynchronous = parcel->readUint32();
@@ -712,11 +723,34 @@
     return NO_ERROR;
 }
 
+void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_state_t& state) {
+    if (!(state.what & layer_state_t::eBufferChanged)) {
+        return;
+    }
+
+    auto listener = state.bufferData.releaseBufferListener;
+    sp<Fence> fence =
+            state.bufferData.acquireFence ? state.bufferData.acquireFence : Fence::NO_FENCE;
+    if (state.bufferData.releaseBufferEndpoint ==
+        IInterface::asBinder(TransactionCompletedListener::getIInstance())) {
+        // if the callback is in process, run on a different thread to avoid any lock contigency
+        // issues in the client.
+        SurfaceComposerClient::getDefault()
+                ->mReleaseCallbackThread
+                .addReleaseCallback(state.bufferData.generateReleaseCallbackId(), fence);
+    } else {
+        listener->onReleaseBuffer(state.bufferData.generateReleaseCallbackId(), fence, UINT_MAX);
+    }
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
     for (auto const& [handle, composerState] : other.mComposerStates) {
         if (mComposerStates.count(handle) == 0) {
             mComposerStates[handle] = composerState;
         } else {
+            if (composerState.state.what & layer_state_t::eBufferChanged) {
+                releaseBufferIfOverwriting(mComposerStates[handle].state);
+            }
             mComposerStates[handle].state.merge(composerState.state);
         }
     }
@@ -792,7 +826,7 @@
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
     sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true,
-                            uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
+                            uncacheBuffer, false, {}, generateId());
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -805,7 +839,7 @@
         layer_state_t* s = &(mComposerStates[handle].state);
         if (!(s->what & layer_state_t::eBufferChanged)) {
             continue;
-        } else if (s->what & layer_state_t::eCachedBufferChanged) {
+        } else if (s->bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged)) {
             // If eBufferChanged and eCachedBufferChanged are both trued then that means
             // we already cached the buffer in a previous call to cacheBuffers, perhaps
             // from writeToParcel on a Transaction that was merged in to this one.
@@ -814,23 +848,22 @@
 
         // Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste
         // time trying to cache them.
-        if (!s->buffer) {
+        if (!s->bufferData.buffer) {
             continue;
         }
 
         uint64_t cacheId = 0;
-        status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId);
+        status_t ret = BufferCache::getInstance().getCacheId(s->bufferData.buffer, &cacheId);
         if (ret == NO_ERROR) {
             // Cache-hit. Strip the buffer and send only the id.
-            s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
-            s->buffer = nullptr;
+            s->bufferData.buffer = nullptr;
         } else {
             // Cache-miss. Include the buffer and send the new cacheId.
-            cacheId = BufferCache::getInstance().cache(s->buffer);
+            cacheId = BufferCache::getInstance().cache(s->bufferData.buffer);
         }
-        s->what |= layer_state_t::eCachedBufferChanged;
-        s->cachedBuffer.token = BufferCache::getInstance().getToken();
-        s->cachedBuffer.id = cacheId;
+        s->bufferData.flags |= BufferData::BufferDataChange::cachedBufferChanged;
+        s->bufferData.cachedBuffer.token = BufferCache::getInstance().getToken();
+        s->bufferData.cachedBuffer.id = cacheId;
 
         // If we have more buffers than the size of the cache, we should stop caching so we don't
         // evict other buffers in this transaction
@@ -1071,7 +1104,8 @@
     }
     if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
         (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
-        (mask & layer_state_t::eEnableBackpressure)) {
+        (mask & layer_state_t::eEnableBackpressure) ||
+        (mask & layer_state_t::eLayerIsDisplayDecoration)) {
         s->what |= layer_state_t::eFlagsChanged;
     }
     s->flags &= ~mask;
@@ -1112,7 +1146,7 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayerStack(
-        const sp<SurfaceControl>& sc, uint32_t layerStack) {
+        const sp<SurfaceControl>& sc, ui::LayerStack layerStack) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1288,73 +1322,79 @@
     return *this;
 }
 
+std::optional<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffer(
+        const sp<SurfaceControl>& sc) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        return std::nullopt;
+    }
+    if (!(s->what & layer_state_t::eBufferChanged)) {
+        return std::nullopt;
+    }
+
+    BufferData bufferData = s->bufferData;
+
+    TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(
+            bufferData.generateReleaseCallbackId());
+    BufferData emptyBufferData;
+    s->what &= ~layer_state_t::eBufferChanged;
+    s->bufferData = emptyBufferData;
+
+    mContainsBuffer = false;
+    return bufferData;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
-        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, const ReleaseCallbackId& id,
+        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+        const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
         ReleaseBufferCallback callback) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    removeReleaseBufferCallback(s);
-    s->what |= layer_state_t::eBufferChanged;
-    s->buffer = buffer;
-    s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
+    releaseBufferIfOverwriting(*s);
+
+    BufferData bufferData;
+    bufferData.buffer = buffer;
+    if (frameNumber) {
+        bufferData.frameNumber = *frameNumber;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+    }
+    if (fence) {
+        bufferData.acquireFence = *fence;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+    }
+    bufferData.releaseBufferEndpoint =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
     if (mIsAutoTimestamp) {
         mDesiredPresentTime = systemTime();
     }
-    setReleaseBufferCallback(s, id, callback);
-
+    setReleaseBufferCallback(&bufferData, callback);
+    s->what |= layer_state_t::eBufferChanged;
+    s->bufferData = bufferData;
     registerSurfaceControlForCallback(sc);
 
     mContainsBuffer = true;
     return *this;
 }
 
-void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
-    if (!s->releaseBufferListener) {
-        return;
-    }
-
-    s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
-    s->releaseBufferListener = nullptr;
-    auto listener = TransactionCompletedListener::getInstance();
-    listener->removeReleaseBufferCallback(s->releaseCallbackId);
-    s->releaseCallbackId = ReleaseCallbackId::INVALID_ID;
-}
-
-void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
-                                                                  const ReleaseCallbackId& id,
+void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData,
                                                                   ReleaseBufferCallback callback) {
     if (!callback) {
         return;
     }
 
-    if (!s->buffer) {
+    if (!bufferData->buffer) {
         ALOGW("Transaction::setReleaseBufferCallback"
               "ignored trying to set a callback on a null buffer.");
         return;
     }
 
-    s->what |= layer_state_t::eReleaseBufferListenerChanged;
-    s->releaseBufferListener = TransactionCompletedListener::getIInstance();
-    s->releaseCallbackId = id;
+    bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance();
     auto listener = TransactionCompletedListener::getInstance();
-    listener->setReleaseBufferCallback(id, callback);
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
-        const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eAcquireFenceChanged;
-    s->acquireFence = fence;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
+    listener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(), callback);
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace(
@@ -1506,20 +1546,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber(
-        const sp<SurfaceControl>& sc, uint64_t frameNumber) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-
-    s->what |= layer_state_t::eFrameNumberChanged;
-    s->frameNumber = frameNumber;
-
-    return *this;
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
         const sp<SurfaceControl>& sc, const WindowInfo& info) {
     layer_state_t* s = getLayerState(sc);
@@ -1792,7 +1818,7 @@
 }
 
 void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& token,
-        uint32_t layerStack) {
+                                                              ui::LayerStack layerStack) {
     DisplayState& s(getDisplayState(token));
     s.layerStack = layerStack;
     s.what |= DisplayState::eLayerStackChanged;
@@ -2190,6 +2216,12 @@
                                                                           lightRadius);
 }
 
+bool SurfaceComposerClient::getDisplayDecorationSupport(const sp<IBinder>& displayToken) {
+    bool support = false;
+    ComposerService::getComposerService()->getDisplayDecorationSupport(displayToken, &support);
+    return support;
+}
+
 int SurfaceComposerClient::getGPUContextPriority() {
     return ComposerService::getComposerService()->getGPUContextPriority();
 }
@@ -2216,12 +2248,12 @@
     return s->captureDisplay(captureArgs, captureListener);
 }
 
-status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+status_t ScreenshotClient::captureDisplay(DisplayId displayId,
                                           const sp<IScreenCaptureListener>& captureListener) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
 
-    return s->captureDisplay(displayOrLayerStack, captureListener);
+    return s->captureDisplay(displayId, captureListener);
 }
 
 status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
@@ -2232,4 +2264,43 @@
     return s->captureLayers(captureArgs, captureListener);
 }
 
+// ---------------------------------------------------------------------------------
+
+void ReleaseCallbackThread::addReleaseCallback(const ReleaseCallbackId callbackId,
+                                               sp<Fence> releaseFence) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    if (!mStarted) {
+        mThread = std::thread(&ReleaseCallbackThread::threadMain, this);
+        mStarted = true;
+    }
+
+    mCallbackInfos.emplace(callbackId, std::move(releaseFence));
+    mReleaseCallbackPending.notify_one();
+}
+
+void ReleaseCallbackThread::threadMain() {
+    const auto listener = TransactionCompletedListener::getInstance();
+    std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> callbackInfos;
+    while (true) {
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            callbackInfos = std::move(mCallbackInfos);
+            mCallbackInfos = {};
+        }
+
+        while (!callbackInfos.empty()) {
+            auto [callbackId, releaseFence] = callbackInfos.front();
+            listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+            callbackInfos.pop();
+        }
+
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            if (mCallbackInfos.size() == 0) {
+                mReleaseCallbackPending.wait(lock);
+            }
+        }
+    }
+}
+
 } // namespace android
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index b2ef7aa..8d356aa 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -43,6 +43,14 @@
     return flags.test(Flag::SPLIT_TOUCH);
 }
 
+bool WindowInfo::isSpy() const {
+    return inputFeatures.test(Feature::SPY);
+}
+
+bool WindowInfo::interceptsStylus() const {
+    return inputFeatures.test(Feature::INTERCEPTS_STYLUS);
+}
+
 bool WindowInfo::overlaps(const WindowInfo* other) const {
     return frameLeft < other->frameRight && frameRight > other->frameLeft &&
             frameTop < other->frameBottom && frameBottom > other->frameTop;
@@ -54,12 +62,11 @@
             info.frameLeft == frameLeft && info.frameTop == frameTop &&
             info.frameRight == frameRight && info.frameBottom == frameBottom &&
             info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
-            info.transform == transform && info.displayOrientation == displayOrientation &&
-            info.displayWidth == displayWidth && info.displayHeight == displayHeight &&
-            info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
-            info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
-            info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
-            info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+            info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+            info.visible == visible && info.trustedOverlay == trustedOverlay &&
+            info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+            info.hasWallpaper == hasWallpaper && info.paused == paused &&
+            info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
             info.packageName == packageName && info.inputFeatures == inputFeatures &&
             info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
             info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
@@ -97,9 +104,6 @@
         parcel->writeFloat(transform.dtdy()) ?:
         parcel->writeFloat(transform.dsdy()) ?:
         parcel->writeFloat(transform.ty()) ?:
-        parcel->writeUint32(displayOrientation) ?:
-        parcel->writeInt32(displayWidth) ?:
-        parcel->writeInt32(displayHeight) ?:
         parcel->writeBool(visible) ?:
         parcel->writeBool(focusable) ?:
         parcel->writeBool(hasWallpaper) ?:
@@ -155,9 +159,6 @@
         parcel->readFloat(&dtdy) ?:
         parcel->readFloat(&dsdy) ?:
         parcel->readFloat(&ty) ?:
-        parcel->readUint32(&displayOrientation) ?:
-        parcel->readInt32(&displayWidth) ?:
-        parcel->readInt32(&displayHeight) ?:
         parcel->readBool(&visible) ?:
         parcel->readBool(&focusable) ?:
         parcel->readBool(&hasWallpaper) ?:
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index c00a438..c32b9ab 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -19,6 +19,7 @@
 
 namespace android {
 
+using gui::DisplayInfo;
 using gui::IWindowInfosReportedListener;
 using gui::WindowInfo;
 using gui::WindowInfosListener;
@@ -65,7 +66,7 @@
 }
 
 binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
-        const std::vector<WindowInfo>& windowInfos,
+        const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
         const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
     std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>>
             windowInfosListeners;
@@ -78,7 +79,7 @@
     }
 
     for (auto listener : windowInfosListeners) {
-        listener->onWindowInfosChanged(windowInfos);
+        listener->onWindowInfosChanged(windowInfos, displayInfos);
     }
 
     if (windowInfosReportedListener) {
diff --git a/libs/ui/Size.cpp b/libs/gui/aidl/android/gui/BitTube.aidl
similarity index 74%
copy from libs/ui/Size.cpp
copy to libs/gui/aidl/android/gui/BitTube.aidl
index d2996d1..6b0595e 100644
--- a/libs/ui/Size.cpp
+++ b/libs/gui/aidl/android/gui/BitTube.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+package android.gui;
 
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+parcelable BitTube cpp_header "private/gui/BitTube.h";
diff --git a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
new file mode 100644
index 0000000..9f41593
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.BitTube;
+
+/** @hide */
+interface IDisplayEventConnection {
+    /*
+     * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
+     * descriptor of outChannel will be initialized, and this effectively "steals" the receive
+     * channel from the remote end (such that the remote end can only use its send channel).
+     */
+    void stealReceiveChannel(out BitTube outChannel);
+
+    /*
+     * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
+     * A value of 2 returns every other event, etc. A value of 0 returns no event unless
+     * requestNextVsync() has been called.
+     */
+    void setVsyncRate(in int count);
+
+    /*
+     * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
+     */
+    oneway void requestNextVsync(); // Asynchronous
+}
diff --git a/libs/ui/Size.cpp b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
similarity index 74%
rename from libs/ui/Size.cpp
rename to libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
index d2996d1..00a3959 100644
--- a/libs/ui/Size.cpp
+++ b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+package android.gui;
 
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+/** @hide */
+oneway interface IRegionSamplingListener {
+    void onSampleCollected(float medianLuma);
+}
diff --git a/libs/ui/Size.cpp b/libs/gui/android/gui/DisplayInfo.aidl
similarity index 67%
copy from libs/ui/Size.cpp
copy to libs/gui/android/gui/DisplayInfo.aidl
index d2996d1..30c0885 100644
--- a/libs/ui/Size.cpp
+++ b/libs/gui/android/gui/DisplayInfo.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+package android.gui;
 
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+parcelable DisplayInfo cpp_header "gui/DisplayInfo.h";
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index d4553ca..a5b2762 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,11 +16,12 @@
 
 package android.gui;
 
+import android.gui.DisplayInfo;
 import android.gui.IWindowInfosReportedListener;
 import android.gui.WindowInfo;
 
 /** @hide */
 oneway interface IWindowInfosListener
 {
-    void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+    void onWindowInfosChanged(in WindowInfo[] windowInfos, in DisplayInfo[] displayInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
 }
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index f9e40ec..f77cfe6 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -81,6 +81,7 @@
         return mProducer;
     }
     sp<Surface> getSurface(bool includeSurfaceControlHandle);
+    bool isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const;
 
     void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
     void onFrameReplaced(const BufferItem& item) override;
@@ -90,16 +91,13 @@
 
     void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
                                       const std::vector<SurfaceControlStats>& stats);
-    void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
-            const std::vector<SurfaceControlStats>& stats);
+    virtual void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+                                     const std::vector<SurfaceControlStats>& stats);
     void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
-                               uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
-    void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
-                                     uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
-    void setNextTransaction(SurfaceComposerClient::Transaction *t);
+                               std::optional<uint32_t> currentMaxAcquiredBufferCount);
+    void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true);
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
-    void setTransactionCompleteCallback(uint64_t frameNumber,
-                                        std::function<void(int64_t)>&& transactionCompleteCallback);
+    void applyPendingTransactions(uint64_t frameNumber);
 
     void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
                 SurfaceComposerClient::Transaction* outTransaction = nullptr);
@@ -110,9 +108,8 @@
     void setSidebandStream(const sp<NativeHandle>& stream);
 
     uint32_t getLastTransformHint() const;
-    void flushShadowQueue();
-
     uint64_t getLastAcquiredFrameNum();
+    void abandon();
 
     virtual ~BLASTBufferQueue();
 
@@ -132,9 +129,14 @@
     bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
     bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
     static PixelFormat convertBufferFormat(PixelFormat& format);
+    void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
+            REQUIRES(mMutex);
 
-    void flushShadowQueueLocked() REQUIRES(mMutex);
+    void flushShadowQueue() REQUIRES(mMutex);
     void acquireAndReleaseBuffer() REQUIRES(mMutex);
+    void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
+            REQUIRES(mMutex);
+    void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
 
     std::string mName;
     // Represents the queued buffer count from buffer queue,
@@ -144,15 +146,15 @@
     std::string mQueuedBufferTrace;
     sp<SurfaceControl> mSurfaceControl;
 
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     std::condition_variable mCallbackCV;
 
     // BufferQueue internally allows 1 more than
     // the max to be acquired
     int32_t mMaxAcquiredBuffers = 1;
 
-    int32_t mNumFrameAvailable GUARDED_BY(mMutex);
-    int32_t mNumAcquired GUARDED_BY(mMutex);
+    int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0;
+    int32_t mNumAcquired GUARDED_BY(mMutex) = 0;
 
     // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
     // buffer or the buffer has been presented and a new buffer is ready to be presented.
@@ -165,12 +167,6 @@
     struct ReleasedBuffer {
         ReleaseCallbackId callbackId;
         sp<Fence> releaseFence;
-        bool operator==(const ReleasedBuffer& rhs) const {
-            // Only compare Id so if we somehow got two callbacks
-            // with different fences we don't decrement mNumAcquired
-            // too far.
-            return rhs.callbackId == callbackId;
-        }
     };
     std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex);
 
@@ -217,7 +213,7 @@
     sp<IGraphicBufferProducer> mProducer;
     sp<BLASTBufferItemConsumer> mBufferItemConsumer;
 
-    SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+    SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
     std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
             mPendingTransactions GUARDED_BY(mMutex);
 
@@ -231,9 +227,6 @@
     // Tracks the last acquired frame number
     uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
 
-    std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
-    uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
-
     // Queues up transactions using this token in SurfaceFlinger. This prevents queued up
     // transactions from other parts of the client from blocking this transaction.
     const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder();
@@ -251,7 +244,11 @@
     std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
 
     uint32_t mCurrentMaxAcquiredBufferCount;
-    bool mWaitForTransactionCallback = false;
+    bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
+
+    // Flag to determine if syncTransaction should only acquire a single buffer and then clear or
+    // continue to acquire buffers until explicitly cleared
+    bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 08f3597..40621dd 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -17,6 +17,7 @@
 #include <gui/DisplayEventReceiver.h>
 #include <utils/Log.h>
 #include <utils/Looper.h>
+#include <array>
 
 namespace android {
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -33,6 +34,26 @@
 
     // The current frame interval in ns when this frame was scheduled.
     int64_t frameInterval = 0;
+
+    struct FrameTimeline {
+        // The Vsync Id corresponsing to this vsync event. This will be used to
+        // populate ISurfaceComposer::setFrameTimelineVsync and
+        // SurfaceComposerClient::setFrameTimelineVsync
+        int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+        // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+        // frame by (both on the CPU and the GPU)
+        int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+        // The anticipated Vsync present time.
+        int64_t expectedPresentTime = 0;
+    };
+
+    // Sorted possible frame timelines.
+    std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines;
+
+    // Index into the frameTimelines that represents the platform's preferred frame timeline.
+    size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max();
 };
 
 class DisplayEventDispatcher : public LooperCallback {
@@ -76,5 +97,8 @@
 
     bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
                               uint32_t* outCount, VsyncEventData* outVsyncEventData);
+
+    void populateFrameTimelines(const DisplayEventReceiver::Event& event,
+                                VsyncEventData* outVsyncEventData) const;
 };
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0dffbde..456bbfb 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -33,7 +33,7 @@
 
 // ----------------------------------------------------------------------------
 
-class IDisplayEventConnection;
+using gui::IDisplayEventConnection;
 
 namespace gui {
 class BitTube;
@@ -49,6 +49,9 @@
 // ----------------------------------------------------------------------------
 class DisplayEventReceiver {
 public:
+    // Max amount of frame timelines is arbitrarily set to be reasonable.
+    static constexpr int64_t kFrameTimelinesLength = 7;
+
     enum {
         DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
         DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -77,6 +80,12 @@
             nsecs_t deadlineTimestamp __attribute__((aligned(8)));
             nsecs_t frameInterval __attribute__((aligned(8)));
             int64_t vsyncId;
+            size_t preferredFrameTimelineIndex __attribute__((aligned(8)));
+            struct FrameTimeline {
+                nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+                nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+                int64_t vsyncId;
+            } frameTimelines[kFrameTimelinesLength];
         };
 
         struct Hotplug {
diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h
new file mode 100644
index 0000000..74f33a2
--- /dev/null
+++ b/libs/gui/include/gui/DisplayInfo.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <gui/constants.h>
+#include <ui/Transform.h>
+
+namespace android::gui {
+
+/*
+ * Describes information about a display that can have windows in it.
+ *
+ * This should only be used by InputFlinger to support raw coordinates in logical display space.
+ */
+struct DisplayInfo : public Parcelable {
+    int32_t displayId = ADISPLAY_ID_NONE;
+
+    // Logical display dimensions.
+    int32_t logicalWidth = 0;
+    int32_t logicalHeight = 0;
+
+    // The display transform. This takes display coordinates to logical display coordinates.
+    ui::Transform transform;
+
+    status_t writeToParcel(android::Parcel*) const override;
+
+    status_t readFromParcel(const android::Parcel*) override;
+};
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/FrameTimelineInfo.h b/libs/gui/include/gui/FrameTimelineInfo.h
index a23c202..255ce56 100644
--- a/libs/gui/include/gui/FrameTimelineInfo.h
+++ b/libs/gui/include/gui/FrameTimelineInfo.h
@@ -36,6 +36,9 @@
     // not directly vendor available.
     int32_t inputEventId = 0;
 
+    // The current time in nanoseconds the application started to render the frame.
+    int64_t startTimeNanos = 0;
+
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
 
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
deleted file mode 100644
index cff22a3..0000000
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-#include <gui/ISurfaceComposer.h>
-#include <utils/Errors.h>
-
-#include <cstdint>
-
-namespace android {
-
-namespace gui {
-class BitTube;
-} // namespace gui
-
-class IDisplayEventConnection : public IInterface {
-public:
-    DECLARE_META_INTERFACE(DisplayEventConnection)
-
-    /*
-     * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
-     * descriptor of outChannel will be initialized, and this effectively "steals" the receive
-     * channel from the remote end (such that the remote end can only use its send channel).
-     */
-    virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0;
-
-    /*
-     * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
-     * A value of 2 returns every other event, etc. A value of 0 returns no event unless
-     * requestNextVsync() has been called.
-     */
-    virtual status_t setVsyncRate(uint32_t count) = 0;
-
-    /*
-     * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
-     */
-    virtual void requestNextVsync() = 0; // Asynchronous
-};
-
-class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
-public:
-    BnDisplayEventConnection()
-          : SafeBnInterface<IDisplayEventConnection>("BnDisplayEventConnection") {}
-
-    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                        uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
deleted file mode 100644
index 1803d9a..0000000
--- a/libs/gui/include/gui/IRegionSamplingListener.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-
-namespace android {
-
-class IRegionSamplingListener : public IInterface {
-public:
-    DECLARE_META_INTERFACE(RegionSamplingListener)
-
-    virtual void onSampleCollected(float medianLuma) = 0;
-};
-
-class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
-public:
-    BnRegionSamplingListener()
-          : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
-
-    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                        uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index cd289cb..69dce9d 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -17,8 +17,10 @@
 #pragma once
 
 #include <android/gui/DisplayBrightness.h>
+#include <android/gui/IDisplayEventConnection.h>
 #include <android/gui/IFpsListener.h>
 #include <android/gui/IHdrLayerInfoListener.h>
+#include <android/gui/IRegionSamplingListener.h>
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
 #include <android/gui/ITunnelModeEnabledListener.h>
@@ -60,13 +62,13 @@
 struct LayerCaptureArgs;
 class LayerDebugInfo;
 class HdrCapabilities;
-class IDisplayEventConnection;
 class IGraphicBufferProducer;
 class ISurfaceComposerClient;
-class IRegionSamplingListener;
 class Rect;
 enum class FrameEvent;
 
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
 using gui::IScreenCaptureListener;
 
 namespace ui {
@@ -116,6 +118,11 @@
 
     using EventRegistrationFlags = Flags<EventRegistration>;
 
+    template <typename T>
+    struct SpHash {
+        size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
+    };
+
     /*
      * Create a connection with SurfaceFlinger.
      */
@@ -241,24 +248,17 @@
      * The subregion can be optionally rotated.  It will also be scaled to
      * match the size of the output buffer.
      */
-    virtual status_t captureDisplay(const DisplayCaptureArgs& args,
-                                    const sp<IScreenCaptureListener>& captureListener) = 0;
+    virtual status_t captureDisplay(const DisplayCaptureArgs&,
+                                    const sp<IScreenCaptureListener>&) = 0;
 
-    virtual status_t captureDisplay(uint64_t displayOrLayerStack,
-                                    const sp<IScreenCaptureListener>& captureListener) = 0;
-
-    template <class AA>
-    struct SpHash {
-        size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); }
-    };
+    virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;
 
     /**
      * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
      * This requires READ_FRAME_BUFFER permission. This function will fail if there
      * is a secure window on screen
      */
-    virtual status_t captureLayers(const LayerCaptureArgs& args,
-                                   const sp<IScreenCaptureListener>& captureListener) = 0;
+    virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;
 
     /* Clears the frame statistics for animations.
      *
@@ -508,20 +508,28 @@
                                              float lightRadius) = 0;
 
     /*
+     * Gets whether a display supports DISPLAY_DECORATION layers.
+     *
+     * displayToken
+     *      The token of the display.
+     * outSupport
+     *      An output parameter for whether the display supports
+     *      DISPLAY_DECORATION layers.
+     *
+     * Returns NO_ERROR upon success. Otherwise,
+     *      NAME_NOT_FOUND if the display is invalid, or
+     *      BAD_VALUE      if the output parameter is invalid.
+     */
+    virtual status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                                 bool* outSupport) const = 0;
+
+    /*
      * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
      */
     virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                                   int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
 
     /*
-     * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
-     * surface flinger will freely switch between frame rates in any way it sees fit, regardless of
-     * the current restrictions applied by DisplayManager. This is useful to get consistent behavior
-     * for tests. Release the token by releasing the returned IBinder reference.
-     */
-    virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
-
-    /*
      * Sets the frame timeline vsync info received from choreographer that corresponds to next
      * buffer submitted on that surface.
      */
@@ -618,6 +626,7 @@
         GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
         SET_GAME_CONTENT_TYPE,
         SET_FRAME_RATE,
+        // Deprecated. Use DisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
         ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
         SET_FRAME_TIMELINE_INFO,
         ADD_TRANSACTION_TRACE_LISTENER,
@@ -635,6 +644,7 @@
         ADD_WINDOW_INFOS_LISTENER,
         REMOVE_WINDOW_INFOS_LISTENER,
         GET_PRIMARY_PHYSICAL_DISPLAY_ID,
+        GET_DISPLAY_DECORATION_SUPPORT,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 937095c..0df5822 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -192,7 +192,6 @@
     virtual void onTransactionCompleted(ListenerStats stats) = 0;
 
     virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
-                                 uint32_t transformHint,
                                  uint32_t currentMaxAcquiredBufferCount) = 0;
 };
 
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index de14b3d..27f4d37 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -59,4 +59,14 @@
     std::string itemToString(uint32_t key, const char* separator) const;
 };
 
+// Keep in sync with the GameManager.java constants.
+enum class GameMode : int32_t {
+    Unsupported = 0,
+    Standard = 1,
+    Performance = 2,
+    Battery = 3,
+
+    ftl_last = Battery
+};
+
 } // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 03e4aac..e48f52d 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -36,6 +36,7 @@
 #include <math/vec3.h>
 #include <ui/BlurRegion.h>
 #include <ui/GraphicTypes.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Rotation.h>
@@ -57,6 +58,41 @@
     bool isValid() const { return token != nullptr; }
 };
 
+struct BufferData {
+    enum class BufferDataChange : uint32_t {
+        fenceChanged = 0x01,
+        frameNumberChanged = 0x02,
+        cachedBufferChanged = 0x04,
+    };
+
+    sp<GraphicBuffer> buffer;
+    sp<Fence> acquireFence;
+
+    // Used by BlastBufferQueue to forward the framenumber generated by the
+    // graphics producer.
+    uint64_t frameNumber = 0;
+
+    // Listens to when the buffer is safe to be released. This is used for blast
+    // layers only. The callback includes a release fence as well as the graphic
+    // buffer id to identify the buffer.
+    sp<ITransactionCompletedListener> releaseBufferListener = nullptr;
+
+    // Stores which endpoint the release information should be sent to. We don't want to send the
+    // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
+    // was called with.
+    sp<IBinder> releaseBufferEndpoint;
+
+    Flags<BufferDataChange> flags;
+
+    client_cache_t cachedBuffer;
+
+    // Generates the release callback id based on the buffer id and frame number.
+    // This is used as an identifier when release callbacks are invoked.
+    ReleaseCallbackId generateReleaseCallbackId() const;
+    status_t write(Parcel& output) const;
+    status_t read(const Parcel& input);
+};
+
 /*
  * Used to communicate layer information between SurfaceFlinger and its clients.
  */
@@ -70,6 +106,7 @@
         // set. This blocks the client until all the buffers have been presented. If the buffers
         // have presentation timestamps, then we may drop buffers.
         eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE
+        eLayerIsDisplayDecoration = 0x200,  // DISPLAY_DECORATION
     };
 
     enum {
@@ -81,7 +118,7 @@
         eTransparentRegionChanged = 0x00000020,
         eFlagsChanged = 0x00000040,
         eLayerStackChanged = 0x00000080,
-        eReleaseBufferListenerChanged = 0x00000400,
+        /* unused 0x00000400, */
         eShadowRadiusChanged = 0x00000800,
         eLayerCreated = 0x00001000,
         eBufferCropChanged = 0x00002000,
@@ -93,7 +130,7 @@
         eTransformToDisplayInverseChanged = 0x00080000,
         eCropChanged = 0x00100000,
         eBufferChanged = 0x00200000,
-        eAcquireFenceChanged = 0x00400000,
+        /* unused 0x00400000, */
         eDataspaceChanged = 0x00800000,
         eHdrMetadataChanged = 0x01000000,
         eSurfaceDamageRegionChanged = 0x02000000,
@@ -104,7 +141,7 @@
         eInputInfoChanged = 0x40000000,
         eCornerRadiusChanged = 0x80000000,
         eDestinationFrameChanged = 0x1'00000000,
-        eCachedBufferChanged = 0x2'00000000,
+        /* unused = 0x2'00000000, */
         eBackgroundColorChanged = 0x4'00000000,
         eMetadataChanged = 0x8'00000000,
         eColorSpaceAgnosticChanged = 0x10'00000000,
@@ -113,7 +150,7 @@
         eBackgroundBlurRadiusChanged = 0x80'00000000,
         eProducerDisconnect = 0x100'00000000,
         eFixedTransformHintChanged = 0x200'00000000,
-        eFrameNumberChanged = 0x400'00000000,
+        /* unused 0x400'00000000, */
         eBlurRegionsChanged = 0x800'00000000,
         eAutoRefreshChanged = 0x1000'00000000,
         eStretchChanged = 0x2000'00000000,
@@ -145,7 +182,7 @@
     int32_t z;
     uint32_t w;
     uint32_t h;
-    uint32_t layerStack;
+    ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
     float alpha;
     uint32_t flags;
     uint32_t mask;
@@ -167,9 +204,7 @@
     uint32_t transform;
     bool transformToDisplayInverse;
     Rect crop;
-    Rect orientedDisplaySpaceRect;
-    sp<GraphicBuffer> buffer;
-    sp<Fence> acquireFence;
+    BufferData bufferData;
     ui::Dataspace dataspace;
     HdrMetadata hdrMetadata;
     Region surfaceDamageRegion;
@@ -180,8 +215,6 @@
 
     sp<gui::WindowInfoHandle> windowInfoHandle = new gui::WindowInfoHandle();
 
-    client_cache_t cachedBuffer;
-
     LayerMetadata metadata;
 
     // The following refer to the alpha, and dataspace, respectively of
@@ -215,10 +248,6 @@
     // otherwise the value will be a valid ui::Rotation.
     ui::Transform::RotationFlags fixedTransformHint;
 
-    // Used by BlastBufferQueue to forward the framenumber generated by the
-    // graphics producer.
-    uint64_t frameNumber;
-
     // Indicates that the consumer should acquire the next frame as soon as it
     // can and not wait for a frame to become available. This is only relevant
     // in shared buffer mode.
@@ -234,22 +263,6 @@
     Rect bufferCrop;
     Rect destinationFrame;
 
-    // Listens to when the buffer is safe to be released. This is used for blast
-    // layers only. The callback includes a release fence as well as the graphic
-    // buffer id to identify the buffer.
-    sp<ITransactionCompletedListener> releaseBufferListener;
-
-    // Keeps track of the release callback id associated with the listener. This
-    // is not sent to the server since the id can be reconstructed there. This
-    // is used to remove the old callback from the client process map if it is
-    // overwritten by another setBuffer call.
-    ReleaseCallbackId releaseCallbackId;
-
-    // Stores which endpoint the release information should be sent to. We don't want to send the
-    // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
-    // was called with.
-    sp<IBinder> releaseBufferEndpoint;
-
     // Force inputflinger to drop all input events for the layer and its children.
     gui::DropInputMode dropInputMode;
 };
@@ -272,11 +285,12 @@
     DisplayState();
     void merge(const DisplayState& other);
 
-    uint32_t what;
+    uint32_t what = 0;
+    uint32_t flags = 0;
     sp<IBinder> token;
     sp<IGraphicBufferProducer> surface;
-    uint32_t layerStack;
-    uint32_t flags;
+
+    ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
 
     // These states define how layers are projected onto the physical display.
     //
@@ -290,10 +304,11 @@
     // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
     // 0).
     ui::Rotation orientation = ui::ROTATION_0;
-    Rect layerStackSpaceRect;
-    Rect orientedDisplaySpaceRect;
+    Rect layerStackSpaceRect = Rect::EMPTY_RECT;
+    Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
 
-    uint32_t width, height;
+    uint32_t width = 0;
+    uint32_t height = 0;
 
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index e540351..40d096e 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -429,11 +429,11 @@
     uint32_t mReqHeight;
 
     // mReqFormat is the buffer pixel format that will be requested at the next
-    // deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
+    // dequeue operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
     PixelFormat mReqFormat;
 
     // mReqUsage is the set of buffer usage flags that will be requested
-    // at the next deuque operation. It is initialized to 0.
+    // at the next dequeue operation. It is initialized to 0.
     uint64_t mReqUsage;
 
     // mTimestamp is the timestamp that will be used for the next buffer queue
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0d1d1a3..b739b3c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <set>
+#include <thread>
 #include <unordered_map>
 #include <unordered_set>
 
@@ -50,22 +51,22 @@
 class HdrCapabilities;
 class ISurfaceComposerClient;
 class IGraphicBufferProducer;
-class IRegionSamplingListener;
 class ITunnelModeEnabledListener;
 class Region;
 
+using gui::IRegionSamplingListener;
+
 struct SurfaceControlStats {
     SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
                         const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
-                        uint32_t hint, FrameEventHistoryStats eventStats, uint32_t currentMaxAcquiredBufferCount)
+                        uint32_t hint, FrameEventHistoryStats eventStats)
           : surfaceControl(sc),
             latchTime(latchTime),
             acquireTime(acquireTime),
             presentFence(presentFence),
             previousReleaseFence(prevReleaseFence),
             transformHint(hint),
-            frameEventStats(eventStats),
-            currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {}
+            frameEventStats(eventStats) {}
 
     sp<SurfaceControl> surfaceControl;
     nsecs_t latchTime = -1;
@@ -74,7 +75,6 @@
     sp<Fence> previousReleaseFence;
     uint32_t transformHint = 0;
     FrameEventHistoryStats frameEventStats;
-    uint32_t currentMaxAcquiredBufferCount = 0;
 };
 
 using TransactionCompletedCallbackTakesContext =
@@ -86,7 +86,7 @@
                            const std::vector<SurfaceControlStats>& /*stats*/)>;
 using ReleaseBufferCallback =
         std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
-                           uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount)>;
+                           std::optional<uint32_t> currentMaxAcquiredBufferCount)>;
 
 using SurfaceStatsCallback =
         std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
@@ -95,6 +95,22 @@
 
 // ---------------------------------------------------------------------------
 
+class ReleaseCallbackThread {
+public:
+    void addReleaseCallback(const ReleaseCallbackId, sp<Fence>);
+    void threadMain();
+
+private:
+    std::thread mThread;
+    std::mutex mMutex;
+    bool mStarted GUARDED_BY(mMutex) = false;
+    std::condition_variable mReleaseCallbackPending;
+    std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> mCallbackInfos
+            GUARDED_BY(mMutex);
+};
+
+// ---------------------------------------------------------------------------
+
 class SurfaceComposerClient : public RefBase
 {
     friend class Composer;
@@ -256,6 +272,16 @@
     static status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                             float lightPosY, float lightPosZ, float lightRadius);
 
+    /*
+     * Returns whether a display supports DISPLAY_DECORATION layers.
+     *
+     * displayToken
+     *      The token of the display.
+     *
+     * Returns whether a display supports DISPLAY_DECORATION layers.
+     */
+    static bool getDisplayDecorationSupport(const sp<IBinder>& displayToken);
+
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
@@ -350,8 +376,7 @@
 
     class Transaction : public Parcelable {
     private:
-        static std::atomic<uint32_t> idCounter;
-        int64_t generateId();
+        void releaseBufferIfOverwriting(const layer_state_t& state);
 
     protected:
         std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -401,9 +426,7 @@
 
         void cacheBuffers();
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
-        void setReleaseBufferCallback(layer_state_t*, const ReleaseCallbackId&,
-                                      ReleaseBufferCallback);
-        void removeReleaseBufferCallback(layer_state_t*);
+        void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback);
 
     public:
         Transaction();
@@ -459,7 +482,7 @@
                                              int backgroundBlurRadius);
         Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
                                     const std::vector<BlurRegion>& regions);
-        Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+        Transaction& setLayerStack(const sp<SurfaceControl>&, ui::LayerStack);
         Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
 
         /// Reparents the current layer to the new parent handle. The new parent must not be null.
@@ -475,10 +498,10 @@
         Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
                                                   bool transformToDisplayInverse);
         Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
-                               const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID,
+                               const std::optional<sp<Fence>>& fence = std::nullopt,
+                               const std::optional<uint64_t>& frameNumber = std::nullopt,
                                ReleaseBufferCallback callback = nullptr);
-        Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
-        Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
+        std::optional<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
         Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
         Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
@@ -503,8 +526,6 @@
 
         // ONLY FOR BLAST ADAPTER
         Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
-        // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
-        Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
 
         Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info);
         Transaction& setFocusedWindow(const gui::FocusRequest& request);
@@ -570,7 +591,7 @@
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
-        void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
+        void setDisplayLayerStack(const sp<IBinder>& token, ui::LayerStack);
 
         void setDisplayFlags(const sp<IBinder>& token, uint32_t flags);
 
@@ -630,6 +651,9 @@
     status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
     status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
 
+protected:
+    ReleaseCallbackThread mReleaseCallbackThread;
+
 private:
     virtual void onFirstRef();
 
@@ -642,12 +666,9 @@
 
 class ScreenshotClient {
 public:
-    static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
-                                   const sp<IScreenCaptureListener>& captureListener);
-    static status_t captureDisplay(uint64_t displayOrLayerStack,
-                                   const sp<IScreenCaptureListener>& captureListener);
-    static status_t captureLayers(const LayerCaptureArgs& captureArgs,
-                                  const sp<IScreenCaptureListener>& captureListener);
+    static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+    static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+    static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
 };
 
 // ---------------------------------------------------------------------------
@@ -733,13 +754,14 @@
     void removeSurfaceStatsListener(void* context, void* cookie);
 
     void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
-    void removeReleaseBufferCallback(const ReleaseCallbackId&);
 
     // BnTransactionCompletedListener overrides
     void onTransactionCompleted(ListenerStats stats) override;
-    void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint,
+    void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
                          uint32_t currentMaxAcquiredBufferCount) override;
 
+    void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
+
     // For Testing Only
     static void setInstance(const sp<TransactionCompletedListener>&);
 
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 4727740..2bfaec8 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/gui/TouchOcclusionMode.h>
+#include <android/os/IInputConstants.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <ftl/Flags.h>
@@ -71,8 +72,9 @@
         SLIPPERY = 0x20000000,
         LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
         DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
-    }; // Window types from WindowManager.LayoutParams
+    };
 
+    // Window types from WindowManager.LayoutParams
     enum class Type : int32_t {
         UNKNOWN = 0,
         FIRST_APPLICATION_WINDOW = 1,
@@ -87,45 +89,66 @@
         APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
         APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
         LAST_SUB_WINDOW = 1999,
-        FIRST_SYSTEM_WINDOW = 2000,
-        STATUS_BAR = FIRST_SYSTEM_WINDOW,
-        SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
-        PHONE = FIRST_SYSTEM_WINDOW + 2,
-        SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
-        KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
-        TOAST = FIRST_SYSTEM_WINDOW + 5,
-        SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
-        PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
-        SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
-        KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
-        SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
-        INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
-        INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
-        WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
-        STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
-        SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
-        DRAG = FIRST_SYSTEM_WINDOW + 16,
-        STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
-        POINTER = FIRST_SYSTEM_WINDOW + 18,
-        NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
-        VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
-        BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
-        INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
-        NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
-        MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
-        ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
-        DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
-        ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
-        NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
+
+#define FIRST_SYSTEM_WINDOW_ 2000
+
+        STATUS_BAR = FIRST_SYSTEM_WINDOW_,
+        SEARCH_BAR = FIRST_SYSTEM_WINDOW_ + 1,
+        PHONE = FIRST_SYSTEM_WINDOW_ + 2,
+        SYSTEM_ALERT = FIRST_SYSTEM_WINDOW_ + 3,
+        KEYGUARD = FIRST_SYSTEM_WINDOW_ + 4,
+        TOAST = FIRST_SYSTEM_WINDOW_ + 5,
+        SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 6,
+        PRIORITY_PHONE = FIRST_SYSTEM_WINDOW_ + 7,
+        SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW_ + 8,
+        KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW_ + 9,
+        SYSTEM_ERROR = FIRST_SYSTEM_WINDOW_ + 10,
+        INPUT_METHOD = FIRST_SYSTEM_WINDOW_ + 11,
+        INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW_ + 12,
+        WALLPAPER = FIRST_SYSTEM_WINDOW_ + 13,
+        STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 14,
+        SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 15,
+        DRAG = FIRST_SYSTEM_WINDOW_ + 16,
+        STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW_ + 17,
+        POINTER = FIRST_SYSTEM_WINDOW_ + 18,
+        NAVIGATION_BAR = FIRST_SYSTEM_WINDOW_ + 19,
+        VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW_ + 20,
+        BOOT_PROGRESS = FIRST_SYSTEM_WINDOW_ + 21,
+        INPUT_CONSUMER = FIRST_SYSTEM_WINDOW_ + 22,
+        NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 24,
+        MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 27,
+        ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW_ + 32,
+        DOCK_DIVIDER = FIRST_SYSTEM_WINDOW_ + 34,
+        ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 39,
+        NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW_ + 40,
+
+        FIRST_SYSTEM_WINDOW = FIRST_SYSTEM_WINDOW_,
         LAST_SYSTEM_WINDOW = 2999,
+
+#undef FIRST_SYSTEM_WINDOW_
+
+        // Small range to limit LUT size.
+        ftl_first = FIRST_SYSTEM_WINDOW,
+        ftl_last = FIRST_SYSTEM_WINDOW + 15
     };
 
-    enum class Feature {
-        DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
-        NO_INPUT_CHANNEL = 1u << 1,
-        DISABLE_USER_ACTIVITY = 1u << 2,
-        DROP_INPUT = 1u << 3,
-        DROP_INPUT_IF_OBSCURED = 1u << 4,
+    // This is a conversion of os::IInputConstants::InputFeature to an enum backed by an unsigned
+    // type. This indicates that they are flags, so it can be used with ftl/enum.h.
+    enum class Feature : uint32_t {
+        // clang-format off
+        NO_INPUT_CHANNEL =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::NO_INPUT_CHANNEL),
+        DISABLE_USER_ACTIVITY =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::DISABLE_USER_ACTIVITY),
+        DROP_INPUT =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT),
+        DROP_INPUT_IF_OBSCURED =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT_IF_OBSCURED),
+        SPY =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::SPY),
+        INTERCEPTS_STYLUS =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::INTERCEPTS_STYLUS),
+        // clang-format on
     };
 
     /* These values are filled in by the WM and passed through SurfaceFlinger
@@ -170,13 +193,6 @@
     // Transform applied to individual windows.
     ui::Transform transform;
 
-    // Display orientation as ui::Transform::RotationFlags. Used for compatibility raw coordinates.
-    uint32_t displayOrientation = ui::Transform::ROT_0;
-
-    // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
-    int32_t displayWidth = 0;
-    int32_t displayHeight = 0;
-
     /*
      * This is filled in by the WM relative to the frame and then translated
      * to absolute coordinates by SurfaceFlinger once the frame is computed.
@@ -211,6 +227,10 @@
 
     bool supportsSplitTouch() const;
 
+    bool isSpy() const;
+
+    bool interceptsStylus() const;
+
     bool overlaps(const WindowInfo* other) const;
 
     bool operator==(const WindowInfo& inputChannel) const;
@@ -267,4 +287,4 @@
 
     WindowInfo mInfo;
 };
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
index 8a70b9b..a18a498 100644
--- a/libs/gui/include/gui/WindowInfosListener.h
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <gui/DisplayInfo.h>
 #include <gui/WindowInfo.h>
 #include <utils/RefBase.h>
 
@@ -23,6 +24,7 @@
 
 class WindowInfosListener : public virtual RefBase {
 public:
-    virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0;
+    virtual void onWindowInfosChanged(const std::vector<WindowInfo>&,
+                                      const std::vector<DisplayInfo>&) = 0;
 };
 } // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 7cb96e0..157a804 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -21,7 +21,6 @@
 #include <binder/IBinder.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/WindowInfosListener.h>
-#include <utils/Mutex.h>
 #include <unordered_set>
 
 namespace android {
@@ -30,7 +29,8 @@
 class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
 public:
     static sp<WindowInfosListenerReporter> getInstance();
-    binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos,
+    binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&,
+                                        const std::vector<gui::DisplayInfo>&,
                                         const sp<gui::IWindowInfosReportedListener>&) override;
 
     status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index d2e0426..6403208 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -135,7 +135,7 @@
         void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
                                        nsecs_t latchTime) const {
             const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
-                         transformHint, frameEvents, ignore] = surfaceControlStats;
+                         transformHint, frameEvents] = surfaceControlStats;
 
             ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
                     << "bad acquire time";
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3d26c3d..6dd1073 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -27,6 +27,7 @@
         "BufferQueue_test.cpp",
         "CpuConsumer_test.cpp",
         "EndToEndNativeInputTest.cpp",
+        "DisplayInfo_test.cpp",
         "DisplayedContentSampling_test.cpp",
         "FillBuffer.cpp",
         "GLTest.cpp",
@@ -62,7 +63,7 @@
         "libinput",
         "libui",
         "libutils",
-        "libnativewindow"
+        "libnativewindow",
     ],
 
     header_libs: ["libsurfaceflinger_headers"],
@@ -117,7 +118,7 @@
         "libgui",
         "libui",
         "libutils",
-        "libbufferhubqueue",  // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
+        "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
         "libpdx_default_transport",
     ],
 
@@ -146,5 +147,5 @@
         "liblog",
         "libui",
         "libutils",
-    ]
+    ],
 }
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fc75485..42a32f3 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -66,26 +66,58 @@
     int32_t mNumReleased GUARDED_BY(mMutex) = 0;
 };
 
+class TestBLASTBufferQueue : public BLASTBufferQueue {
+public:
+    TestBLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
+                         int height, int32_t format)
+          : BLASTBufferQueue(name, surface, width, height, format) {}
+
+    void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+                             const std::vector<SurfaceControlStats>& stats) override {
+        BLASTBufferQueue::transactionCallback(latchTime, presentFence, stats);
+        uint64_t frameNumber = stats[0].frameEventStats.frameNumber;
+
+        {
+            std::unique_lock lock{frameNumberMutex};
+            mLastTransactionFrameNumber = frameNumber;
+            mWaitForCallbackCV.notify_all();
+        }
+    }
+
+    void waitForCallback(int64_t frameNumber) {
+        std::unique_lock lock{frameNumberMutex};
+        // Wait until all but one of the submitted buffers have been released.
+        while (mLastTransactionFrameNumber < frameNumber) {
+            mWaitForCallbackCV.wait(lock);
+        }
+    }
+
+private:
+    std::mutex frameNumberMutex;
+    std::condition_variable mWaitForCallbackCV;
+    int64_t mLastTransactionFrameNumber = -1;
+};
+
 class BLASTBufferQueueHelper {
 public:
     BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
-        mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height,
-                                                        PIXEL_FORMAT_RGBA_8888);
+        mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width,
+                                                            height, PIXEL_FORMAT_RGBA_8888);
     }
 
     void update(const sp<SurfaceControl>& sc, int width, int height) {
         mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888);
     }
 
-    void setNextTransaction(Transaction* next) {
-        mBlastBufferQueueAdapter->setNextTransaction(next);
+    void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) {
+        mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer);
     }
 
     int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
 
     int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
 
-    Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
+    Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; }
 
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
         return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
@@ -107,28 +139,17 @@
         }
     }
 
-    void setTransactionCompleteCallback(int64_t frameNumber) {
-        mBlastBufferQueueAdapter->setTransactionCompleteCallback(frameNumber, [&](int64_t frame) {
-            std::unique_lock lock{mMutex};
-            mLastTransactionCompleteFrameNumber = frame;
-            mCallbackCV.notify_all();
-        });
+    void waitForCallback(int64_t frameNumber) {
+        mBlastBufferQueueAdapter->waitForCallback(frameNumber);
     }
 
-    void waitForCallback(int64_t frameNumber) {
-        std::unique_lock lock{mMutex};
-        // Wait until all but one of the submitted buffers have been released.
-        while (mLastTransactionCompleteFrameNumber < frameNumber) {
-            mCallbackCV.wait(lock);
-        }
+    void validateNumFramesSubmitted(int64_t numFramesSubmitted) {
+        std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+        ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
     }
 
 private:
-    sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
-
-    std::mutex mMutex;
-    std::condition_variable mCallbackCV;
-    int64_t mLastTransactionCompleteFrameNumber = -1;
+    sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
 };
 
 class BLASTBufferQueueTest : public ::testing::Test {
@@ -152,7 +173,7 @@
         mDisplayToken = mClient->getInternalDisplayToken();
         ASSERT_NE(nullptr, mDisplayToken.get());
         Transaction t;
-        t.setDisplayLayerStack(mDisplayToken, 0);
+        t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
         t.apply();
         t.clear();
 
@@ -166,7 +187,7 @@
                                                  mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  /*parent*/ nullptr);
-        t.setLayerStack(mSurfaceControl, 0)
+        t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
                 .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
                 .show(mSurfaceControl)
                 .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
@@ -282,7 +303,7 @@
         auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
                                        PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
                                        nullptr, nullptr);
-        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_TRUE(ret == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || ret == NO_ERROR);
         ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf));
 
         uint32_t* bufData;
@@ -321,7 +342,7 @@
     ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
     ASSERT_EQ(mDisplayWidth, adapter.getWidth());
     ASSERT_EQ(mDisplayHeight, adapter.getHeight());
-    ASSERT_EQ(nullptr, adapter.getNextTransaction());
+    ASSERT_EQ(nullptr, adapter.getSyncTransaction());
 }
 
 TEST_F(BLASTBufferQueueTest, Update) {
@@ -342,11 +363,11 @@
     ASSERT_EQ(mDisplayHeight / 2, height);
 }
 
-TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
+TEST_F(BLASTBufferQueueTest, SetSyncTransaction) {
     BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
-    Transaction next;
-    adapter.setNextTransaction(&next);
-    ASSERT_EQ(&next, adapter.getNextTransaction());
+    Transaction sync;
+    adapter.setSyncTransaction(&sync);
+    ASSERT_EQ(&sync, adapter.getSyncTransaction());
 }
 
 TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
@@ -516,7 +537,7 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect);
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
-    t.setLayerStack(bg, 0)
+    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
             .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
@@ -571,7 +592,7 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect);
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
-    t.setLayerStack(bg, 0)
+    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
             .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
@@ -638,7 +659,7 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect);
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
-    t.setLayerStack(bg, 0)
+    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
             .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
@@ -785,8 +806,8 @@
     sp<IGraphicBufferProducer> igbProducer;
     setUpProducer(adapter, igbProducer);
 
-    Transaction next;
-    adapter.setNextTransaction(&next);
+    Transaction sync;
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
     // queue non sync buffer, so this one should get blocked
@@ -795,14 +816,14 @@
     queueBuffer(igbProducer, r, g, b, presentTimeDelay);
 
     CallbackHelper transactionCallback;
-    next.addTransactionCompletedCallback(transactionCallback.function,
+    sync.addTransactionCompletedCallback(transactionCallback.function,
                                          transactionCallback.getContext())
             .apply();
 
     CallbackData callbackData;
     transactionCallback.getCallbackData(&callbackData);
 
-    // capture screen and verify that it is red
+    // capture screen and verify that it is green
     ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -825,16 +846,16 @@
 
     Transaction mainTransaction;
 
-    Transaction next;
-    adapter.setNextTransaction(&next);
+    Transaction sync;
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
 
-    adapter.setNextTransaction(&next);
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, r, g, b, 0);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
     // Expect 1 buffer to be released even before sending to SurfaceFlinger
     mProducerListener->waitOnNumberReleased(1);
 
@@ -865,24 +886,24 @@
 
     Transaction mainTransaction;
 
-    Transaction next;
+    Transaction sync;
     // queue a sync transaction
-    adapter.setNextTransaction(&next);
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
 
-    // queue another buffer without setting next transaction
+    // queue another buffer without setting sync transaction
     queueBuffer(igbProducer, 0, 0, 255, 0);
 
     // queue another sync transaction
-    adapter.setNextTransaction(&next);
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, r, g, b, 0);
     // Expect 1 buffer to be released because the non sync transaction should merge
     // with the sync
     mProducerListener->waitOnNumberReleased(1);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
     // Expect 2 buffers to be released due to merging the two syncs.
     mProducerListener->waitOnNumberReleased(2);
 
@@ -913,26 +934,26 @@
 
     Transaction mainTransaction;
 
-    Transaction next;
+    Transaction sync;
     // queue a sync transaction
-    adapter.setNextTransaction(&next);
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
 
-    // queue a few buffers without setting next transaction
+    // queue a few buffers without setting sync transaction
     queueBuffer(igbProducer, 0, 0, 255, 0);
     queueBuffer(igbProducer, 0, 0, 255, 0);
     queueBuffer(igbProducer, 0, 0, 255, 0);
 
     // queue another sync transaction
-    adapter.setNextTransaction(&next);
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, r, g, b, 0);
     // Expect 3 buffers to be released because the non sync transactions should merge
     // with the sync
     mProducerListener->waitOnNumberReleased(3);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
     // Expect 4 buffers to be released due to merging the two syncs.
     mProducerListener->waitOnNumberReleased(4);
 
@@ -970,14 +991,14 @@
     // Send a buffer to SF
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
-    Transaction next;
+    Transaction sync;
     // queue a sync transaction
-    adapter.setNextTransaction(&next);
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, 0, 255, 0, 0);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
 
-    // queue a few buffers without setting next transaction
+    // queue a few buffers without setting sync transaction
     queueBuffer(igbProducer, 0, 0, 255, 0);
     queueBuffer(igbProducer, 0, 0, 255, 0);
     queueBuffer(igbProducer, 0, 0, 255, 0);
@@ -986,13 +1007,13 @@
     mainTransaction.apply();
 
     // queue another sync transaction
-    adapter.setNextTransaction(&next);
+    adapter.setSyncTransaction(&sync);
     queueBuffer(igbProducer, r, g, b, 0);
     // Expect 2 buffers to be released because the non sync transactions should merge
     // with the sync
     mProducerListener->waitOnNumberReleased(3);
 
-    mainTransaction.merge(std::move(next));
+    mainTransaction.merge(std::move(sync));
 
     CallbackHelper transactionCallback;
     mainTransaction
@@ -1009,6 +1030,133 @@
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
 
+TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    Transaction next;
+    adapter.setSyncTransaction(&next, false);
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+    // There should only be one frame submitted since the first frame will be released.
+    adapter.validateNumFramesSubmitted(1);
+    adapter.setSyncTransaction(nullptr);
+
+    // queue non sync buffer, so this one should get blocked
+    // Add a present delay to allow the first screenshot to get taken.
+    nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+    queueBuffer(igbProducer, 255, 0, 0, presentTimeDelay);
+
+    CallbackHelper transactionCallback;
+    next.addTransactionCompletedCallback(transactionCallback.function,
+                                         transactionCallback.getContext())
+            .apply();
+
+    CallbackData callbackData;
+    transactionCallback.getCallbackData(&callbackData);
+
+    // capture screen and verify that it is blue
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+
+    mProducerListener->waitOnNumberReleased(2);
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+// 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
+// approach for the client is to recreate the blastbufferqueue when the surfacecontrol updates.
+TEST_F(BLASTBufferQueueTest, DISABLED_DisconnectProducerTest) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    std::vector<sp<SurfaceControl>> surfaceControls;
+    sp<IGraphicBufferProducer> igbProducer;
+    for (int i = 0; i < 10; i++) {
+        sp<SurfaceControl> sc =
+                mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
+                                       PIXEL_FORMAT_RGBA_8888,
+                                       ISurfaceComposerClient::eFXSurfaceBufferState,
+                                       /*parent*/ nullptr);
+        Transaction()
+                .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .show(mSurfaceControl)
+                .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+                .apply(true);
+        surfaceControls.push_back(sc);
+        adapter.update(sc, mDisplayWidth, mDisplayHeight);
+
+        setUpProducer(adapter, igbProducer);
+        Transaction next;
+        queueBuffer(igbProducer, 0, 255, 0, 0);
+        queueBuffer(igbProducer, 0, 0, 255, 0);
+        adapter.setSyncTransaction(&next, false);
+        queueBuffer(igbProducer, 255, 0, 0, 0);
+
+        CallbackHelper transactionCallback;
+        next.addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+                .apply();
+
+        CallbackData callbackData;
+        transactionCallback.getCallbackData(&callbackData);
+        // capture screen and verify that it is red
+        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_NO_FATAL_FAILURE(
+                checkScreenCapture(255, 0, 0,
+                                   {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+        igbProducer->disconnect(NATIVE_WINDOW_API_CPU);
+    }
+}
+
+// See DISABLED_DisconnectProducerTest
+TEST_F(BLASTBufferQueueTest, DISABLED_UpdateSurfaceControlTest) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    std::vector<sp<SurfaceControl>> surfaceControls;
+    sp<IGraphicBufferProducer> igbProducer;
+    for (int i = 0; i < 10; i++) {
+        sp<SurfaceControl> sc =
+                mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
+                                       PIXEL_FORMAT_RGBA_8888,
+                                       ISurfaceComposerClient::eFXSurfaceBufferState,
+                                       /*parent*/ nullptr);
+        Transaction()
+                .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .show(mSurfaceControl)
+                .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+                .apply(true);
+        surfaceControls.push_back(sc);
+        adapter.update(sc, mDisplayWidth, mDisplayHeight);
+        setUpProducer(adapter, igbProducer);
+
+        Transaction next;
+        queueBuffer(igbProducer, 0, 255, 0, 0);
+        queueBuffer(igbProducer, 0, 0, 255, 0);
+        adapter.setSyncTransaction(&next, false);
+        queueBuffer(igbProducer, 255, 0, 0, 0);
+
+        CallbackHelper transactionCallback;
+        next.addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+                .apply();
+
+        CallbackData callbackData;
+        transactionCallback.getCallbackData(&callbackData);
+        // capture screen and verify that it is red
+        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_NO_FATAL_FAILURE(
+                checkScreenCapture(255, 0, 0,
+                                   {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+    }
+}
+
 class TestProducerListener : public BnProducerListener {
 public:
     sp<IGraphicBufferProducer> mIgbp;
@@ -1070,7 +1218,7 @@
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
     ASSERT_NE(nullptr, bgSurface.get());
     Transaction t;
-    t.setLayerStack(bgSurface, 0)
+    t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK)
             .show(bgSurface)
             .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
             .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
@@ -1366,7 +1514,6 @@
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
     nsecs_t requestedPresentTimeA = 0;
     nsecs_t postedTimeA = 0;
-    adapter.setTransactionCompleteCallback(1);
     setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
     history.applyDelta(qbOutput.frameTimestamps);
 
@@ -1435,7 +1582,6 @@
     // queue another buffer so the first can be dropped
     nsecs_t requestedPresentTimeB = 0;
     nsecs_t postedTimeB = 0;
-    adapter.setTransactionCompleteCallback(2);
     presentTime = systemTime() + std::chrono::nanoseconds(1ms).count();
     setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true,
                         presentTime);
@@ -1501,7 +1647,6 @@
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
     nsecs_t requestedPresentTimeA = 0;
     nsecs_t postedTimeA = 0;
-    adapter.setTransactionCompleteCallback(1);
     setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
     history.applyDelta(qbOutput.frameTimestamps);
     adapter.waitForCallback(1);
diff --git a/libs/gui/tests/DisplayInfo_test.cpp b/libs/gui/tests/DisplayInfo_test.cpp
new file mode 100644
index 0000000..df3329c
--- /dev/null
+++ b/libs/gui/tests/DisplayInfo_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+
+#include <gui/DisplayInfo.h>
+
+namespace android {
+
+using gui::DisplayInfo;
+
+namespace test {
+
+TEST(DisplayInfo, Parcelling) {
+    DisplayInfo info;
+    info.displayId = 42;
+    info.logicalWidth = 99;
+    info.logicalHeight = 78;
+    info.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+
+    Parcel p;
+    info.writeToParcel(&p);
+    p.setDataPosition(0);
+
+    DisplayInfo info2;
+    info2.readFromParcel(&p);
+    ASSERT_EQ(info.displayId, info2.displayId);
+    ASSERT_EQ(info.logicalWidth, info2.logicalWidth);
+    ASSERT_EQ(info.logicalHeight, info2.logicalHeight);
+    ASSERT_EQ(info.transform, info2.transform);
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 5daef0d..6f1263b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -46,6 +46,8 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include <private/android_filesystem_config.h>
+
 using android::os::IInputFlinger;
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -179,6 +181,25 @@
         EXPECT_EQ(flags, mev->getFlags() & flags);
     }
 
+    void expectTapInDisplayCoordinates(int displayX, int displayY) {
+        InputEvent *ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+        MotionEvent *mev = static_cast<MotionEvent *>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+        const PointerCoords &coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
+        EXPECT_EQ(displayX, coords.getX());
+        EXPECT_EQ(displayY, coords.getY());
+        EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
+
+        ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+        mev = static_cast<MotionEvent *>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+        EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
+    }
+
     void expectKey(uint32_t keycode) {
         InputEvent *ev = consumeEvent();
         ASSERT_NE(ev, nullptr);
@@ -220,7 +241,7 @@
         t.apply(true);
     }
 
-    void requestFocus() {
+    void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
         SurfaceComposerClient::Transaction t;
         FocusRequest request;
         request.token = mInputInfo.token;
@@ -228,7 +249,7 @@
         request.focusedToken = nullptr;
         request.focusedWindowName = "";
         request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
-        request.displayId = 0;
+        request.displayId = displayId;
         t.setFocusedWindow(request);
         t.apply(true);
     }
@@ -255,11 +276,6 @@
 
         mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
 
-        // TODO: Fill in from SF?
-        mInputInfo.ownerPid = 11111;
-        mInputInfo.ownerUid = 11111;
-        mInputInfo.displayId = 0;
-
         InputApplicationInfo aInfo;
         aInfo.token = new BBinder();
         aInfo.name = "Test app info";
@@ -373,21 +389,31 @@
     int32_t mBufferPostDelay;
 };
 
-void injectTap(int x, int y) {
-    char *buf1, *buf2;
+void injectTapOnDisplay(int x, int y, int displayId) {
+    char *buf1, *buf2, *bufDisplayId;
     asprintf(&buf1, "%d", x);
     asprintf(&buf2, "%d", y);
+    asprintf(&bufDisplayId, "%d", displayId);
     if (fork() == 0) {
-        execlp("input", "input", "tap", buf1, buf2, NULL);
+        execlp("input", "input", "-d", bufDisplayId, "tap", buf1, buf2, NULL);
+    }
+}
+
+void injectTap(int x, int y) {
+    injectTapOnDisplay(x, y, ADISPLAY_ID_DEFAULT);
+}
+
+void injectKeyOnDisplay(uint32_t keycode, int displayId) {
+    char *buf1, *bufDisplayId;
+    asprintf(&buf1, "%d", keycode);
+    asprintf(&bufDisplayId, "%d", displayId);
+    if (fork() == 0) {
+        execlp("input", "input", "-d", bufDisplayId, "keyevent", buf1, NULL);
     }
 }
 
 void injectKey(uint32_t keycode) {
-    char *buf1;
-    asprintf(&buf1, "%d", keycode);
-    if (fork() == 0) {
-        execlp("input", "input", "keyevent", buf1, NULL);
-    }
+    injectKeyOnDisplay(keycode, ADISPLAY_ID_NONE);
 }
 
 TEST_F(InputSurfacesTest, can_receive_input) {
@@ -564,7 +590,7 @@
     bufferSurface->expectTap(1, 1);
 }
 
-TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
     std::unique_ptr<BlastInputSurface> bufferSurface =
             BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
@@ -579,7 +605,7 @@
     bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
 
     injectTap(11, 11);
-    bufferSurface->expectTap(1, 1);
+    bgSurface->expectTap(1, 1);
 }
 
 TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) {
@@ -960,4 +986,145 @@
     injectKey(AKEYCODE_V);
     EXPECT_EQ(surface->consumeEvent(100), nullptr);
 }
+
+class MultiDisplayTests : public InputSurfacesTest {
+public:
+    MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
+    void TearDown() override {
+        for (auto &token : mVirtualDisplays) {
+            SurfaceComposerClient::destroyDisplay(token);
+        }
+        InputSurfacesTest::TearDown();
+    }
+
+    void createDisplay(int32_t width, int32_t height, bool isSecure, ui::LayerStack layerStack,
+                       bool receivesInput = true, int32_t offsetX = 0, int32_t offsetY = 0) {
+        sp<IGraphicBufferConsumer> consumer;
+        sp<IGraphicBufferProducer> producer;
+        BufferQueue::createBufferQueue(&producer, &consumer);
+        consumer->setConsumerName(String8("Virtual disp consumer"));
+        consumer->setDefaultBufferSize(width, height);
+        mProducers.push_back(producer);
+
+        std::string name = "VirtualDisplay";
+        name += std::to_string(mVirtualDisplays.size());
+        sp<IBinder> token = SurfaceComposerClient::createDisplay(String8(name.c_str()), isSecure);
+        SurfaceComposerClient::Transaction t;
+        t.setDisplaySurface(token, producer);
+        t.setDisplayFlags(token, receivesInput ? 0x01 /* DisplayDevice::eReceivesInput */ : 0);
+        t.setDisplayLayerStack(token, layerStack);
+        t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height},
+                               {offsetX, offsetY, offsetX + width, offsetY + height});
+        t.apply(true);
+
+        mVirtualDisplays.push_back(token);
+    }
+
+    std::vector<sp<IBinder>> mVirtualDisplays;
+    std::vector<sp<IGraphicBufferProducer>> mProducers;
+};
+
+TEST_F(MultiDisplayTests, drop_input_if_layer_on_invalid_display) {
+    ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+    // Do not create a display associated with the LayerStack.
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+    surface->showAt(100, 100);
+
+    injectTapOnDisplay(101, 101, layerStack.id);
+    surface->requestFocus(layerStack.id);
+    injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(MultiDisplayTests, virtual_display_receives_input) {
+    ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+    createDisplay(1000, 1000, false /*isSecure*/, layerStack);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+    surface->showAt(100, 100);
+
+    injectTapOnDisplay(101, 101, layerStack.id);
+    surface->expectTap(1, 1);
+
+    surface->requestFocus(layerStack.id);
+    surface->assertFocusChange(true);
+    injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+    surface->expectKey(AKEYCODE_V);
+}
+
+/**
+ * When multiple DisplayDevices are mapped to the same layerStack, use the configuration for the
+ * display that can receive input.
+ */
+TEST_F(MultiDisplayTests, many_to_one_display_mapping) {
+    ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+    createDisplay(1000, 1000, false /*isSecure*/, layerStack, false /*receivesInput*/,
+                  100 /*offsetX*/, 100 /*offsetY*/);
+    createDisplay(1000, 1000, false /*isSecure*/, layerStack, true /*receivesInput*/,
+                  200 /*offsetX*/, 200 /*offsetY*/);
+    createDisplay(1000, 1000, false /*isSecure*/, layerStack, false /*receivesInput*/,
+                  300 /*offsetX*/, 300 /*offsetY*/);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+    surface->showAt(10, 10);
+
+    // Input injection happens in logical display coordinates.
+    injectTapOnDisplay(11, 11, layerStack.id);
+    // Expect that the display transform for the display that receives input was used.
+    surface->expectTapInDisplayCoordinates(211, 211);
+
+    surface->requestFocus(layerStack.id);
+    surface->assertFocusChange(true);
+    injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+}
+
+TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) {
+    ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+    createDisplay(1000, 1000, false /*isSecure*/, layerStack);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) {
+        t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+        t.setLayerStack(sc, layerStack);
+    });
+    surface->showAt(100, 100);
+
+    injectTapOnDisplay(101, 101, layerStack.id);
+
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+    surface->requestFocus(layerStack.id);
+    surface->assertFocusChange(true);
+    injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) {
+    ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+
+    // Create the secure display as system, because only certain users can create secure displays.
+    seteuid(AID_SYSTEM);
+    createDisplay(1000, 1000, true /*isSecure*/, layerStack);
+    // Change the uid back to root.
+    seteuid(AID_ROOT);
+
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) {
+        t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+        t.setLayerStack(sc, layerStack);
+    });
+    surface->showAt(100, 100);
+
+    injectTapOnDisplay(101, 101, layerStack.id);
+    EXPECT_NE(surface->consumeEvent(), nullptr);
+    EXPECT_NE(surface->consumeEvent(), nullptr);
+
+    surface->requestFocus(layerStack.id);
+    surface->assertFocusChange(true);
+    injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+
+    surface->expectKey(AKEYCODE_V);
+}
+
 } // namespace android::test
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index 6746b0a..c9106be 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -17,9 +17,9 @@
 #include <gtest/gtest.h>
 #include <thread>
 
+#include <android/gui/BnRegionSamplingListener.h>
 #include <binder/ProcessState.h>
 #include <gui/DisplayEventReceiver.h>
-#include <gui/IRegionSamplingListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -135,12 +135,13 @@
     std::atomic<bool> poll_{true};
 };
 
-struct Listener : BnRegionSamplingListener {
-    void onSampleCollected(float medianLuma) override {
+struct Listener : android::gui::BnRegionSamplingListener {
+    binder::Status onSampleCollected(float medianLuma) override {
         std::unique_lock<decltype(mutex)> lk(mutex);
         received = true;
         mLuma = medianLuma;
         cv.notify_all();
+        return binder::Status::ok();
     };
     bool wait_event(std::chrono::milliseconds timeout) {
         std::unique_lock<decltype(mutex)> lk(mutex);
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 0cd150d..a083a22 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -20,9 +20,9 @@
 #include <chrono>
 #include <thread>
 
+#include <android/gui/BnRegionSamplingListener.h>
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
-#include <gui/IRegionSamplingListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
@@ -33,7 +33,7 @@
 
 namespace android {
 
-class Button : public BnRegionSamplingListener {
+class Button : public gui::BnRegionSamplingListener {
 public:
     Button(const char* name, const Rect& samplingArea) {
         sp<SurfaceComposerClient> client = new SurfaceComposerClient;
@@ -99,9 +99,10 @@
                 .apply();
     }
 
-    void onSampleCollected(float medianLuma) override {
+    binder::Status onSampleCollected(float medianLuma) override {
         ATRACE_CALL();
         setColor(medianLuma);
+        return binder::Status::ok();
     }
 
     sp<SurfaceComposerClient> mClient;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c745505..d5e089a 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -19,11 +19,11 @@
 #include <gtest/gtest.h>
 
 #include <SurfaceFlingerProperties.h>
+#include <android/gui/IDisplayEventConnection.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <binder/ProcessState.h>
 #include <configstore/Utils.h>
 #include <gui/BufferItemConsumer.h>
-#include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
@@ -45,6 +45,8 @@
 // retrieve wide-color and hdr settings from configstore
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
 using ui::ColorMode;
 
 using Transaction = SurfaceComposerClient::Transaction;
@@ -753,21 +755,19 @@
     }
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
-    status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
-                            const sp<IScreenCaptureListener>& /* captureListener */) override {
-        return NO_ERROR;
-    }
     void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
-    status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
-                            const sp<IScreenCaptureListener>& /* captureListener */) override {
+
+    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {
         return NO_ERROR;
     }
-    virtual status_t captureLayers(
-            const LayerCaptureArgs& /* captureArgs */,
-            const sp<IScreenCaptureListener>& /* captureListener */) override {
+    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {
         return NO_ERROR;
     }
+    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override {
+        return NO_ERROR;
+    }
+
     status_t clearAnimationFrameStats() override { return NO_ERROR; }
     status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
         return NO_ERROR;
@@ -882,12 +882,13 @@
         return NO_ERROR;
     }
 
-    status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
-                          int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
+    status_t getDisplayDecorationSupport(const sp<IBinder>& /*displayToken*/,
+                                         bool* /*outSupport*/) const override {
         return NO_ERROR;
     }
 
-    status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override {
+    status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
+                          int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
         return NO_ERROR;
     }
 
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index a4f436c..dcdf76f 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -60,9 +60,6 @@
     i.globalScaleFactor = 0.3;
     i.alpha = 0.7;
     i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
-    i.displayOrientation = ui::Transform::ROT_0;
-    i.displayWidth = 1000;
-    i.displayHeight = 2000;
     i.visible = false;
     i.focusable = false;
     i.hasWallpaper = false;
@@ -100,8 +97,6 @@
     ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
     ASSERT_EQ(i.alpha, i2.alpha);
     ASSERT_EQ(i.transform, i2.transform);
-    ASSERT_EQ(i.displayWidth, i2.displayWidth);
-    ASSERT_EQ(i.displayHeight, i2.displayHeight);
     ASSERT_EQ(i.visible, i2.visible);
     ASSERT_EQ(i.focusable, i2.focusable);
     ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 5f440b7..84dba84 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,12 +20,11 @@
 #include <attestation/HmacKeyManager.h>
 #include <cutils/compiler.h>
 #include <inttypes.h>
-#include <limits.h>
 #include <string.h>
 
-#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <gui/constants.h>
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
@@ -43,15 +42,6 @@
 
 namespace {
 
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-bool isPerWindowInputRotationEnabled() {
-    static const bool PER_WINDOW_INPUT_ROTATION =
-            base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
-
-    return PER_WINDOW_INPUT_ROTATION;
-}
-
 float transformAngle(const ui::Transform& transform, float angleRadians) {
     // Construct and transform a vector oriented at the specified clockwise angle from vertical.
     // Coordinate system: down is increasing Y, right is increasing X.
@@ -71,40 +61,17 @@
     return atan2f(transformedPoint.x, -transformedPoint.y);
 }
 
-// Rotates the given point to the specified orientation. If the display width and height are
-// provided, the point is rotated in the screen space. Otherwise, the point is rotated about the
-// origin. This helper is used to avoid the extra overhead of creating new Transforms.
-vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0,
-                 int32_t displayHeight = 0) {
-    if (orientation == ui::Transform::ROT_0) {
-        return {x, y};
-    }
-
-    vec2 xy(x, y);
-    if (orientation == ui::Transform::ROT_90) {
-        xy.x = displayHeight - y;
-        xy.y = x;
-    } else if (orientation == ui::Transform::ROT_180) {
-        xy.x = displayWidth - x;
-        xy.y = displayHeight - y;
-    } else if (orientation == ui::Transform::ROT_270) {
-        xy.x = y;
-        xy.y = displayWidth - x;
-    }
-    return xy;
+bool shouldDisregardTransformation(uint32_t source) {
+    // Do not apply any transformations to axes from joysticks or touchpads.
+    return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
+            isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
 }
 
-vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) {
-    const vec2 transformedXy = transform.transform(x, y);
-    const vec2 transformedOrigin = transform.transform(0, 0);
-    return transformedXy - transformedOrigin;
-}
-
-bool shouldDisregardWindowTranslation(uint32_t source) {
+bool shouldDisregardOffset(uint32_t source) {
     // Pointer events are the only type of events that refer to absolute coordinates on the display,
     // so we should apply the entire window transform. For other types of events, we should make
     // sure to not apply the window translation/offset.
-    return (source & AINPUT_SOURCE_CLASS_POINTER) == 0;
+    return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
 }
 
 } // namespace
@@ -148,6 +115,12 @@
 
 // --- InputEvent ---
 
+vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
+    const vec2 transformedXy = transform.transform(xy);
+    const vec2 transformedOrigin = transform.transform(0, 0);
+    return transformedXy - transformedOrigin;
+}
+
 const char* inputEventTypeToString(int32_t type) {
     switch (type) {
         case AINPUT_EVENT_TYPE_KEY: {
@@ -165,16 +138,62 @@
         case AINPUT_EVENT_TYPE_DRAG: {
             return "DRAG";
         }
+        case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+            return "TOUCH_MODE";
+        }
     }
     return "UNKNOWN";
 }
 
+std::string inputEventSourceToString(int32_t source) {
+    if (source == AINPUT_SOURCE_UNKNOWN) {
+        return "UNKNOWN";
+    }
+    if (source == static_cast<int32_t>(AINPUT_SOURCE_ANY)) {
+        return "ANY";
+    }
+    static const std::map<int32_t, const char*> SOURCES{
+            {AINPUT_SOURCE_KEYBOARD, "KEYBOARD"},
+            {AINPUT_SOURCE_DPAD, "DPAD"},
+            {AINPUT_SOURCE_GAMEPAD, "GAMEPAD"},
+            {AINPUT_SOURCE_TOUCHSCREEN, "TOUCHSCREEN"},
+            {AINPUT_SOURCE_MOUSE, "MOUSE"},
+            {AINPUT_SOURCE_STYLUS, "STYLUS"},
+            {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BLUETOOTH_STYLUS"},
+            {AINPUT_SOURCE_TRACKBALL, "TRACKBALL"},
+            {AINPUT_SOURCE_MOUSE_RELATIVE, "MOUSE_RELATIVE"},
+            {AINPUT_SOURCE_TOUCHPAD, "TOUCHPAD"},
+            {AINPUT_SOURCE_TOUCH_NAVIGATION, "TOUCH_NAVIGATION"},
+            {AINPUT_SOURCE_JOYSTICK, "JOYSTICK"},
+            {AINPUT_SOURCE_HDMI, "HDMI"},
+            {AINPUT_SOURCE_SENSOR, "SENSOR"},
+            {AINPUT_SOURCE_ROTARY_ENCODER, "ROTARY_ENCODER"},
+    };
+    std::string result;
+    for (const auto& [source_entry, str] : SOURCES) {
+        if ((source & source_entry) == source_entry) {
+            if (!result.empty()) {
+                result += " | ";
+            }
+            result += str;
+        }
+    }
+    if (result.empty()) {
+        result = StringPrintf("0x%08x", source);
+    }
+    return result;
+}
+
+bool isFromSource(uint32_t source, uint32_t test) {
+    return (source & test) == test;
+}
+
 VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
     return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
              event.getSource(), event.getDisplayId()},
             event.getAction(),
-            event.getDownTime(),
             event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+            event.getDownTime(),
             event.getKeyCode(),
             event.getScanCode(),
             event.getMetaState(),
@@ -187,8 +206,8 @@
             event.getRawX(0),
             event.getRawY(0),
             event.getActionMasked(),
-            event.getDownTime(),
             event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+            event.getDownTime(),
             event.getMetaState(),
             event.getButtonState()};
 }
@@ -325,15 +344,6 @@
     scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);
 }
 
-void PointerCoords::scale(float globalScaleFactor) {
-    scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
-}
-
-void PointerCoords::applyOffset(float xOffset, float yOffset) {
-    setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset);
-    setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
-}
-
 #ifdef __linux__
 status_t PointerCoords::readFromParcel(Parcel* parcel) {
     bits = parcel->readInt64();
@@ -427,8 +437,7 @@
                              int32_t buttonState, MotionClassification classification,
                              const ui::Transform& transform, float xPrecision, float yPrecision,
                              float rawXCursorPosition, float rawYCursorPosition,
-                             uint32_t displayOrientation, int32_t displayWidth,
-                             int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+                             const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
                              size_t pointerCount, const PointerProperties* pointerProperties,
                              const PointerCoords* pointerCoords) {
     InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -444,9 +453,7 @@
     mYPrecision = yPrecision;
     mRawXCursorPosition = rawXCursorPosition;
     mRawYCursorPosition = rawYCursorPosition;
-    mDisplayOrientation = displayOrientation;
-    mDisplayWidth = displayWidth;
-    mDisplayHeight = displayHeight;
+    mRawTransform = rawTransform;
     mDownTime = downTime;
     mPointerProperties.clear();
     mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -470,9 +477,7 @@
     mYPrecision = other->mYPrecision;
     mRawXCursorPosition = other->mRawXCursorPosition;
     mRawYCursorPosition = other->mRawYCursorPosition;
-    mDisplayOrientation = other->mDisplayOrientation;
-    mDisplayWidth = other->mDisplayWidth;
-    mDisplayHeight = other->mDisplayHeight;
+    mRawTransform = other->mRawTransform;
     mDownTime = other->mDownTime;
     mPointerProperties = other->mPointerProperties;
 
@@ -497,6 +502,24 @@
     mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
 }
 
+int MotionEvent::getSurfaceRotation() const {
+    // The surface rotation is the rotation from the window's coordinate space to that of the
+    // display. Since the event's transform takes display space coordinates to window space, the
+    // returned surface rotation is the inverse of the rotation for the surface.
+    switch (mTransform.getOrientation()) {
+        case ui::Transform::ROT_0:
+            return DISPLAY_ORIENTATION_0;
+        case ui::Transform::ROT_90:
+            return DISPLAY_ORIENTATION_270;
+        case ui::Transform::ROT_180:
+            return DISPLAY_ORIENTATION_180;
+        case ui::Transform::ROT_270:
+            return DISPLAY_ORIENTATION_90;
+        default:
+            return -1;
+    }
+}
+
 float MotionEvent::getXCursorPosition() const {
     vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
     return vals.x;
@@ -533,56 +556,14 @@
 
 float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
                                              size_t historicalIndex) const {
-    const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
-    if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis);
-
-    if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
-        // For compatibility, convert raw coordinates into "oriented screen space". Once app
-        // developers are educated about getRaw, we can consider removing this.
-        const vec2 xy = shouldDisregardWindowTranslation(mSource)
-                ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY())
-                : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth,
-                              mDisplayHeight);
-        static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
-        return xy[axis];
-    }
-
-    if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
-        // For compatibility, since we convert raw coordinates into "oriented screen space", we
-        // need to convert the relative axes into the same orientation for consistency.
-        const vec2 relativeXy = rotatePoint(mDisplayOrientation,
-                                            coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
-                                            coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
-        return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
-    }
-
-    return coords->getAxisValue(axis);
+    const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+    return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords);
 }
 
 float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
                                           size_t historicalIndex) const {
-    const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
-    if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
-        const vec2 xy = shouldDisregardWindowTranslation(mSource)
-                ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY())
-                : mTransform.transform(coords->getXYValue());
-        static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
-        return xy[axis];
-    }
-
-    if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
-        const vec2 relativeXy =
-                applyTransformWithoutTranslation(mTransform,
-                                                 coords->getAxisValue(
-                                                         AMOTION_EVENT_AXIS_RELATIVE_X),
-                                                 coords->getAxisValue(
-                                                         AMOTION_EVENT_AXIS_RELATIVE_Y));
-        return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
-    }
-
-    return coords->getAxisValue(axis);
+    const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+    return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
 }
 
 ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -603,6 +584,8 @@
 
 void MotionEvent::scale(float globalScaleFactor) {
     mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
+    mRawTransform.set(mRawTransform.tx() * globalScaleFactor,
+                      mRawTransform.ty() * globalScaleFactor);
     mXPrecision *= globalScaleFactor;
     mYPrecision *= globalScaleFactor;
 
@@ -619,15 +602,6 @@
     ui::Transform newTransform;
     newTransform.set(matrix);
     mTransform = newTransform * mTransform;
-
-    // We need to update the AXIS_ORIENTATION value here to maintain the old behavior where the
-    // orientation angle is not affected by the initial transformation set in the MotionEvent.
-    std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
-                  [&newTransform](PointerCoords& c) {
-                      float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
-                      c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
-                                     transformAngle(newTransform, orientation));
-                  });
 }
 
 void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
@@ -704,9 +678,11 @@
     mYPrecision = parcel->readFloat();
     mRawXCursorPosition = parcel->readFloat();
     mRawYCursorPosition = parcel->readFloat();
-    mDisplayOrientation = parcel->readUint32();
-    mDisplayWidth = parcel->readInt32();
-    mDisplayHeight = parcel->readInt32();
+
+    result = android::readFromParcel(mRawTransform, *parcel);
+    if (result != OK) {
+        return result;
+    }
     mDownTime = parcel->readInt64();
 
     mPointerProperties.clear();
@@ -766,9 +742,11 @@
     parcel->writeFloat(mYPrecision);
     parcel->writeFloat(mRawXCursorPosition);
     parcel->writeFloat(mRawYCursorPosition);
-    parcel->writeUint32(mDisplayOrientation);
-    parcel->writeInt32(mDisplayWidth);
-    parcel->writeInt32(mDisplayHeight);
+
+    result = android::writeToParcel(mRawTransform, *parcel);
+    if (result != OK) {
+        return result;
+    }
     parcel->writeInt64(mDownTime);
 
     for (size_t i = 0; i < pointerCount; i++) {
@@ -792,7 +770,7 @@
 #endif
 
 bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
-    if (source & AINPUT_SOURCE_CLASS_POINTER) {
+    if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) {
         // Specifically excludes HOVER_MOVE and SCROLL.
         switch (action & AMOTION_EVENT_ACTION_MASK) {
         case AMOTION_EVENT_ACTION_DOWN:
@@ -830,9 +808,9 @@
         case AMOTION_EVENT_ACTION_OUTSIDE:
             return "OUTSIDE";
         case AMOTION_EVENT_ACTION_POINTER_DOWN:
-            return "POINTER_DOWN";
+            return StringPrintf("POINTER_DOWN(%" PRId32 ")", MotionEvent::getActionIndex(action));
         case AMOTION_EVENT_ACTION_POINTER_UP:
-            return "POINTER_UP";
+            return StringPrintf("POINTER_UP(%" PRId32 ")", MotionEvent::getActionIndex(action));
         case AMOTION_EVENT_ACTION_HOVER_MOVE:
             return "HOVER_MOVE";
         case AMOTION_EVENT_ACTION_SCROLL:
@@ -849,19 +827,61 @@
     return android::base::StringPrintf("%" PRId32, action);
 }
 
+// Apply the given transformation to the point without checking whether the entire transform
+// should be disregarded altogether for the provided source.
+static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
+                                                   const vec2& xy) {
+    return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
+                                         : transform.transform(xy);
+}
+
+vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
+                                         const vec2& xy) {
+    if (shouldDisregardTransformation(source)) {
+        return xy;
+    }
+    return calculateTransformedXYUnchecked(source, transform, xy);
+}
+
+float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
+                                                 const ui::Transform& transform,
+                                                 const PointerCoords& coords) {
+    if (shouldDisregardTransformation(source)) {
+        return coords.getAxisValue(axis);
+    }
+
+    if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
+        const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
+        static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+        return xy[axis];
+    }
+
+    if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
+        const vec2 relativeXy =
+                transformWithoutTranslation(transform,
+                                            {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+                                             coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
+        return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
+    }
+
+    if (axis == AMOTION_EVENT_AXIS_ORIENTATION) {
+        return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+    }
+
+    return coords.getAxisValue(axis);
+}
+
 // --- FocusEvent ---
 
-void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
+void FocusEvent::initialize(int32_t id, bool hasFocus) {
     InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
                            ADISPLAY_ID_NONE, INVALID_HMAC);
     mHasFocus = hasFocus;
-    mInTouchMode = inTouchMode;
 }
 
 void FocusEvent::initialize(const FocusEvent& from) {
     InputEvent::initialize(from);
     mHasFocus = from.mHasFocus;
-    mInTouchMode = from.mInTouchMode;
 }
 
 // --- CaptureEvent ---
@@ -894,6 +914,19 @@
     mY = from.mY;
 }
 
+// --- TouchModeEvent ---
+
+void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) {
+    InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+                           ADISPLAY_ID_NONE, INVALID_HMAC);
+    mIsInTouchMode = isInTouchMode;
+}
+
+void TouchModeEvent::initialize(const TouchModeEvent& from) {
+    InputEvent::initialize(from);
+    mIsInTouchMode = from.mIsInTouchMode;
+}
+
 // --- PooledInputEventFactory ---
 
 PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -948,6 +981,15 @@
     return event;
 }
 
+TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() {
+    if (mTouchModeEventPool.empty()) {
+        return new TouchModeEvent();
+    }
+    TouchModeEvent* event = mTouchModeEventPool.front().release();
+    mTouchModeEventPool.pop();
+    return event;
+}
+
 void PooledInputEventFactory::recycle(InputEvent* event) {
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY:
@@ -981,6 +1023,13 @@
             return;
         }
         break;
+    case AINPUT_EVENT_TYPE_TOUCH_MODE:
+        if (mTouchModeEventPool.size() < mMaxPoolSize) {
+            mTouchModeEventPool.push(
+                    std::unique_ptr<TouchModeEvent>(static_cast<TouchModeEvent*>(event)));
+            return;
+        }
+        break;
     }
     delete event;
 }
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 1aec477..ac84627 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,7 +21,7 @@
 #include <ctype.h>
 
 #include <android-base/stringprintf.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
 
@@ -208,10 +208,8 @@
 
 const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
         int32_t axis, uint32_t source) const {
-    size_t numRanges = mMotionRanges.size();
-    for (size_t i = 0; i < numRanges; i++) {
-        const MotionRange& range = mMotionRanges[i];
-        if (range.axis == axis && range.source == source) {
+    for (const MotionRange& range : mMotionRanges) {
+        if (range.axis == axis && isFromSource(range.source, source)) {
             return &range;
         }
     }
@@ -235,7 +233,7 @@
 void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) {
     if (mSensors.find(info.type) != mSensors.end()) {
         ALOGW("Sensor type %s already exists, will be replaced by new sensor added.",
-              NamedEnum::string(info.type).c_str());
+              ftl::enum_string(info.type).c_str());
     }
     mSensors.insert_or_assign(info.type, info);
 }
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index ea8b9a7..a065ce2 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -30,10 +30,10 @@
 #include <android-base/stringprintf.h>
 #include <binder/Parcel.h>
 #include <cutils/properties.h>
+#include <ftl/enum.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 
-#include <ftl/NamedEnum.h>
 #include <input/InputTransport.h>
 
 using android::base::StringPrintf;
@@ -116,6 +116,7 @@
         case Type::FOCUS:
         case Type::CAPTURE:
         case Type::DRAG:
+        case Type::TOUCH_MODE:
             return true;
         case Type::TIMELINE: {
             const nsecs_t gpuCompletedTime =
@@ -151,6 +152,8 @@
             return sizeof(Header) + body.drag.size();
         case Type::TIMELINE:
             return sizeof(Header) + body.timeline.size();
+        case Type::TOUCH_MODE:
+            return sizeof(Header) + body.touchMode.size();
     }
     return sizeof(Header);
 }
@@ -200,6 +203,8 @@
         case InputMessage::Type::MOTION: {
             // int32_t eventId
             msg->body.motion.eventId = body.motion.eventId;
+            // uint32_t pointerCount
+            msg->body.motion.pointerCount = body.motion.pointerCount;
             // nsecs_t eventTime
             msg->body.motion.eventTime = body.motion.eventTime;
             // int32_t deviceId
@@ -242,14 +247,14 @@
             msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
             // float yCursorPosition
             msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
-            // uint32_t displayOrientation
-            msg->body.motion.displayOrientation = body.motion.displayOrientation;
-            // int32_t displayWidth
-            msg->body.motion.displayWidth = body.motion.displayWidth;
-            // int32_t displayHeight
-            msg->body.motion.displayHeight = body.motion.displayHeight;
-            // uint32_t pointerCount
-            msg->body.motion.pointerCount = body.motion.pointerCount;
+
+            msg->body.motion.dsdxRaw = body.motion.dsdxRaw;
+            msg->body.motion.dtdxRaw = body.motion.dtdxRaw;
+            msg->body.motion.dtdyRaw = body.motion.dtdyRaw;
+            msg->body.motion.dsdyRaw = body.motion.dsdyRaw;
+            msg->body.motion.txRaw = body.motion.txRaw;
+            msg->body.motion.tyRaw = body.motion.tyRaw;
+
             //struct Pointer pointers[MAX_POINTERS]
             for (size_t i = 0; i < body.motion.pointerCount; i++) {
                 // PointerProperties properties
@@ -273,7 +278,6 @@
         case InputMessage::Type::FOCUS: {
             msg->body.focus.eventId = body.focus.eventId;
             msg->body.focus.hasFocus = body.focus.hasFocus;
-            msg->body.focus.inTouchMode = body.focus.inTouchMode;
             break;
         }
         case InputMessage::Type::CAPTURE: {
@@ -293,6 +297,10 @@
             msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
             break;
         }
+        case InputMessage::Type::TOUCH_MODE: {
+            msg->body.touchMode.eventId = body.touchMode.eventId;
+            msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode;
+        }
     }
 }
 
@@ -535,8 +543,8 @@
         std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
         int32_t edgeFlags, int32_t metaState, int32_t buttonState,
         MotionClassification classification, const ui::Transform& transform, float xPrecision,
-        float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation,
-        int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+        float yPrecision, float xCursorPosition, float yCursorPosition,
+        const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
         uint32_t pointerCount, const PointerProperties* pointerProperties,
         const PointerCoords* pointerCoords) {
     if (ATRACE_ENABLED()) {
@@ -596,9 +604,12 @@
     msg.body.motion.yPrecision = yPrecision;
     msg.body.motion.xCursorPosition = xCursorPosition;
     msg.body.motion.yCursorPosition = yCursorPosition;
-    msg.body.motion.displayOrientation = displayOrientation;
-    msg.body.motion.displayWidth = displayWidth;
-    msg.body.motion.displayHeight = displayHeight;
+    msg.body.motion.dsdxRaw = rawTransform.dsdx();
+    msg.body.motion.dtdxRaw = rawTransform.dtdx();
+    msg.body.motion.dtdyRaw = rawTransform.dtdy();
+    msg.body.motion.dsdyRaw = rawTransform.dsdy();
+    msg.body.motion.txRaw = rawTransform.tx();
+    msg.body.motion.tyRaw = rawTransform.ty();
     msg.body.motion.downTime = downTime;
     msg.body.motion.eventTime = eventTime;
     msg.body.motion.pointerCount = pointerCount;
@@ -610,13 +621,10 @@
     return mChannel->sendMessage(&msg);
 }
 
-status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus,
-                                           bool inTouchMode) {
+status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
     if (ATRACE_ENABLED()) {
-        std::string message =
-                StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
-                             mChannel->getName().c_str(), toString(hasFocus),
-                             toString(inTouchMode));
+        std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)",
+                                           mChannel->getName().c_str(), toString(hasFocus));
         ATRACE_NAME(message.c_str());
     }
 
@@ -625,7 +633,6 @@
     msg.header.seq = seq;
     msg.body.focus.eventId = eventId;
     msg.body.focus.hasFocus = hasFocus;
-    msg.body.focus.inTouchMode = inTouchMode;
     return mChannel->sendMessage(&msg);
 }
 
@@ -665,6 +672,22 @@
     return mChannel->sendMessage(&msg);
 }
 
+status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) {
+    if (ATRACE_ENABLED()) {
+        std::string message =
+                StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)",
+                             mChannel->getName().c_str(), toString(isInTouchMode));
+        ATRACE_NAME(message.c_str());
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::TOUCH_MODE;
+    msg.header.seq = seq;
+    msg.body.touchMode.eventId = eventId;
+    msg.body.touchMode.isInTouchMode = isInTouchMode;
+    return mChannel->sendMessage(&msg);
+}
+
 android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
     if (DEBUG_TRANSPORT_ACTIONS) {
         ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
@@ -691,7 +714,7 @@
     }
 
     ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
-          mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+          mChannel->getName().c_str(), ftl::enum_string(msg.header.type).c_str());
     return android::base::Error(UNKNOWN_ERROR);
 }
 
@@ -833,7 +856,7 @@
             case InputMessage::Type::TIMELINE: {
                 LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
                                  "InputConsumer!",
-                                 NamedEnum::string(mMsg.header.type).c_str());
+                                 ftl::enum_string(mMsg.header.type).c_str());
                 break;
             }
 
@@ -866,6 +889,16 @@
                 *outEvent = dragEvent;
                 break;
             }
+
+            case InputMessage::Type::TOUCH_MODE: {
+                TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
+                if (!touchModeEvent) return NO_MEMORY;
+
+                initializeTouchModeEvent(touchModeEvent, &mMsg);
+                *outSeq = mMsg.header.seq;
+                *outEvent = touchModeEvent;
+                break;
+            }
         }
     }
     return OK;
@@ -1333,8 +1366,7 @@
 }
 
 void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
-    event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus,
-                      msg->body.focus.inTouchMode);
+    event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus);
 }
 
 void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
@@ -1358,6 +1390,10 @@
     ui::Transform transform;
     transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
                    msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
+    ui::Transform displayTransform;
+    displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,
+                          msg->body.motion.txRaw, msg->body.motion.dtdyRaw,
+                          msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1});
     event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
                       msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
                       msg->body.motion.actionButton, msg->body.motion.flags,
@@ -1365,9 +1401,12 @@
                       msg->body.motion.buttonState, msg->body.motion.classification, transform,
                       msg->body.motion.xPrecision, msg->body.motion.yPrecision,
                       msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
-                      msg->body.motion.displayOrientation, msg->body.motion.displayWidth,
-                      msg->body.motion.displayHeight, msg->body.motion.downTime,
-                      msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+                      displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,
+                      pointerCount, pointerProperties, pointerCoords);
+}
+
+void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
+    event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode);
 }
 
 void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
@@ -1412,14 +1451,14 @@
     out = out + "mChannel = " + mChannel->getName() + "\n";
     out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
     if (mMsgDeferred) {
-        out = out + "mMsg : " + NamedEnum::string(mMsg.header.type) + "\n";
+        out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n";
     }
     out += "Batches:\n";
     for (const Batch& batch : mBatches) {
         out += "    Batch:\n";
         for (const InputMessage& msg : batch.samples) {
             out += android::base::StringPrintf("        Message %" PRIu32 ": %s ", msg.header.seq,
-                                               NamedEnum::string(msg.header.type).c_str());
+                                               ftl::enum_string(msg.header.type).c_str());
             switch (msg.header.type) {
                 case InputMessage::Type::KEY: {
                     out += android::base::StringPrintf("action=%s keycode=%" PRId32,
@@ -1446,9 +1485,8 @@
                     break;
                 }
                 case InputMessage::Type::FOCUS: {
-                    out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
-                                                       toString(msg.body.focus.hasFocus),
-                                                       toString(msg.body.focus.inTouchMode));
+                    out += android::base::StringPrintf("hasFocus=%s",
+                                                       toString(msg.body.focus.hasFocus));
                     break;
                 }
                 case InputMessage::Type::CAPTURE: {
@@ -1476,6 +1514,11 @@
                                                        presentTime);
                     break;
                 }
+                case InputMessage::Type::TOUCH_MODE: {
+                    out += android::base::StringPrintf("isInTouchMode=%s",
+                                                       toString(msg.body.touchMode.isInTouchMode));
+                    break;
+                }
             }
             out += "\n";
         }
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index c365ab0..7c25cda 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -16,10 +16,8 @@
 
 #define LOG_TAG "KeyLayoutMap"
 
-#include <stdlib.h>
-
 #include <android/keycodes.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
 #include <input/InputEventLabels.h>
 #include <input/KeyLayoutMap.h>
 #include <input/Keyboard.h>
@@ -28,6 +26,10 @@
 #include <utils/Timers.h>
 #include <utils/Tokenizer.h>
 
+#include <cstdlib>
+#include <string_view>
+#include <unordered_map>
+
 // Enables debug output for the parser.
 #define DEBUG_PARSER 0
 
@@ -39,36 +41,38 @@
 
 
 namespace android {
+namespace {
 
-static const char* WHITESPACE = " \t\r";
+constexpr const char* WHITESPACE = " \t\r";
 
-#define SENSOR_ENTRY(type) NamedEnum::string(type), type
-static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST =
-        {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)},
-         {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)},
-         {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)},
-         {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)},
-         {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)},
-         {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)},
-         {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)},
-         {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)},
-         {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)},
-         {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)},
-         {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)},
-         {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)},
-         {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)},
-         {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)},
-         {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)},
-         {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)},
-         {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}};
-
-// --- KeyLayoutMap ---
-
-KeyLayoutMap::KeyLayoutMap() {
+template <InputDeviceSensorType S>
+constexpr auto sensorPair() {
+    return std::make_pair(ftl::enum_name<S>(), S);
 }
 
-KeyLayoutMap::~KeyLayoutMap() {
-}
+static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_LIST =
+        {sensorPair<InputDeviceSensorType::ACCELEROMETER>(),
+         sensorPair<InputDeviceSensorType::MAGNETIC_FIELD>(),
+         sensorPair<InputDeviceSensorType::ORIENTATION>(),
+         sensorPair<InputDeviceSensorType::GYROSCOPE>(),
+         sensorPair<InputDeviceSensorType::LIGHT>(),
+         sensorPair<InputDeviceSensorType::PRESSURE>(),
+         sensorPair<InputDeviceSensorType::TEMPERATURE>(),
+         sensorPair<InputDeviceSensorType::PROXIMITY>(),
+         sensorPair<InputDeviceSensorType::GRAVITY>(),
+         sensorPair<InputDeviceSensorType::LINEAR_ACCELERATION>(),
+         sensorPair<InputDeviceSensorType::ROTATION_VECTOR>(),
+         sensorPair<InputDeviceSensorType::RELATIVE_HUMIDITY>(),
+         sensorPair<InputDeviceSensorType::AMBIENT_TEMPERATURE>(),
+         sensorPair<InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED>(),
+         sensorPair<InputDeviceSensorType::GAME_ROTATION_VECTOR>(),
+         sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(),
+         sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()};
+
+} // namespace
+
+KeyLayoutMap::KeyLayoutMap() = default;
+KeyLayoutMap::~KeyLayoutMap() = default;
 
 base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
                                                                        const char* contents) {
@@ -160,8 +164,8 @@
     const Sensor& sensor = it->second;
 
 #if DEBUG_MAPPING
-    ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode,
-          NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex);
+    ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
+          ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
 #endif
     return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
 }
@@ -513,7 +517,7 @@
 }
 
 static std::optional<InputDeviceSensorType> getSensorType(const char* token) {
-    auto it = SENSOR_LIST.find(std::string(token));
+    auto it = SENSOR_LIST.find(token);
     if (it == SENSOR_LIST.end()) {
         return std::nullopt;
     }
@@ -581,8 +585,8 @@
     int32_t sensorDataIndex = indexOpt.value();
 
 #if DEBUG_PARSER
-    ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code,
-          NamedEnum::string(sensorType).c_str(), sensorDataIndex);
+    ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+          ftl::enum_string(sensorType).c_str(), sensorDataIndex);
 #endif
 
     Sensor sensor;
@@ -591,4 +595,5 @@
     map.emplace(code, sensor);
     return NO_ERROR;
 }
-};
+
+} // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index a44f0b7..a6465ee 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -18,10 +18,10 @@
 //#define LOG_NDEBUG 0
 
 // Log debug messages about velocity tracking.
-#define DEBUG_VELOCITY 0
+static constexpr bool DEBUG_VELOCITY = false;
 
 // Log debug messages about the progress of the algorithm itself.
-#define DEBUG_STRATEGY 0
+static constexpr bool DEBUG_STRATEGY = false;
 
 #include <array>
 #include <inttypes.h>
@@ -30,7 +30,6 @@
 #include <optional>
 
 #include <android-base/stringprintf.h>
-#include <cutils/properties.h>
 #include <input/VelocityTracker.h>
 #include <utils/BitSet.h>
 #include <utils/Timers.h>
@@ -64,7 +63,6 @@
     return sqrtf(r);
 }
 
-#if DEBUG_STRATEGY || DEBUG_VELOCITY
 static std::string vectorToString(const float* a, uint32_t m) {
     std::string str;
     str += "[";
@@ -77,9 +75,11 @@
     str += " ]";
     return str;
 }
-#endif
 
-#if DEBUG_STRATEGY
+static std::string vectorToString(const std::vector<float>& v) {
+    return vectorToString(v.data(), v.size());
+}
+
 static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
     std::string str;
     str = "[";
@@ -99,7 +99,6 @@
     str += " ]";
     return str;
 }
-#endif
 
 
 // --- VelocityTracker ---
@@ -133,12 +132,18 @@
         VelocityTracker::Strategy strategy) {
     switch (strategy) {
         case VelocityTracker::Strategy::IMPULSE:
+            if (DEBUG_STRATEGY) {
+                ALOGI("Initializing impulse strategy");
+            }
             return std::make_unique<ImpulseVelocityTrackerStrategy>();
 
         case VelocityTracker::Strategy::LSQ1:
             return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
 
         case VelocityTracker::Strategy::LSQ2:
+            if (DEBUG_STRATEGY) {
+                ALOGI("Initializing lsq2 strategy");
+            }
             return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
 
         case VelocityTracker::Strategy::LSQ3:
@@ -204,10 +209,10 @@
 
     if ((mCurrentPointerIdBits.value & idBits.value)
             && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
-#if DEBUG_VELOCITY
-        ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
-                (eventTime - mLastEventTime) * 0.000001f);
-#endif
+        if (DEBUG_VELOCITY) {
+            ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
+                  (eventTime - mLastEventTime) * 0.000001f);
+        }
         // We have not received any movements for too long.  Assume that all pointers
         // have stopped.
         mStrategy->clear();
@@ -221,24 +226,24 @@
 
     mStrategy->addMovement(eventTime, idBits, positions);
 
-#if DEBUG_VELOCITY
-    ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d",
-            eventTime, idBits.value, mActivePointerId);
-    for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
-        uint32_t id = iterBits.firstMarkedBit();
-        uint32_t index = idBits.getIndexOfBit(id);
-        iterBits.clearBit(id);
-        Estimator estimator;
-        getEstimator(id, &estimator);
-        ALOGD("  %d: position (%0.3f, %0.3f), "
-                "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
-                id, positions[index].x, positions[index].y,
-                int(estimator.degree),
-                vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
-                vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
-                estimator.confidence);
+    if (DEBUG_VELOCITY) {
+        ALOGD("VelocityTracker: addMovement eventTime=%" PRId64
+              ", idBits=0x%08x, activePointerId=%d",
+              eventTime, idBits.value, mActivePointerId);
+        for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
+            uint32_t id = iterBits.firstMarkedBit();
+            uint32_t index = idBits.getIndexOfBit(id);
+            iterBits.clearBit(id);
+            Estimator estimator;
+            getEstimator(id, &estimator);
+            ALOGD("  %d: position (%0.3f, %0.3f), "
+                  "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
+                  id, positions[index].x, positions[index].y, int(estimator.degree),
+                  vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
+                  vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
+                  estimator.confidence);
+        }
     }
-#endif
 }
 
 void VelocityTracker::addMovement(const MotionEvent* event) {
@@ -419,11 +424,10 @@
 static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
                               const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
     const size_t m = x.size();
-#if DEBUG_STRATEGY
-    ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
-            vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
-            vectorToString(w, m).c_str());
-#endif
+    if (DEBUG_STRATEGY) {
+        ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
+              vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str());
+    }
     LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
 
     // Expand the X vector to a matrix A, pre-multiplied by the weights.
@@ -434,9 +438,9 @@
             a[i][h] = a[i - 1][h] * x[h];
         }
     }
-#if DEBUG_STRATEGY
-    ALOGD("  - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
+    if (DEBUG_STRATEGY) {
+        ALOGD("  - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
+    }
 
     // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
     float q[n][m]; // orthonormal basis, column-major order
@@ -455,9 +459,9 @@
         float norm = vectorNorm(&q[j][0], m);
         if (norm < 0.000001f) {
             // vectors are linearly dependent or zero so no solution
-#if DEBUG_STRATEGY
-            ALOGD("  - no solution, norm=%f", norm);
-#endif
+            if (DEBUG_STRATEGY) {
+                ALOGD("  - no solution, norm=%f", norm);
+            }
             return false;
         }
 
@@ -469,22 +473,22 @@
             r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
         }
     }
-#if DEBUG_STRATEGY
-    ALOGD("  - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
-    ALOGD("  - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
+    if (DEBUG_STRATEGY) {
+        ALOGD("  - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
+        ALOGD("  - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
 
-    // calculate QR, if we factored A correctly then QR should equal A
-    float qr[n][m];
-    for (uint32_t h = 0; h < m; h++) {
-        for (uint32_t i = 0; i < n; i++) {
-            qr[i][h] = 0;
-            for (uint32_t j = 0; j < n; j++) {
-                qr[i][h] += q[j][h] * r[j][i];
+        // calculate QR, if we factored A correctly then QR should equal A
+        float qr[n][m];
+        for (uint32_t h = 0; h < m; h++) {
+            for (uint32_t i = 0; i < n; i++) {
+                qr[i][h] = 0;
+                for (uint32_t j = 0; j < n; j++) {
+                    qr[i][h] += q[j][h] * r[j][i];
+                }
             }
         }
+        ALOGD("  - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
     }
-    ALOGD("  - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
 
     // Solve R B = Qt W Y to find B.  This is easy because R is upper triangular.
     // We just work from bottom-right to top-left calculating B's coefficients.
@@ -500,9 +504,9 @@
         }
         outB[i] /= r[i][i];
     }
-#if DEBUG_STRATEGY
-    ALOGD("  - b=%s", vectorToString(outB, n).c_str());
-#endif
+    if (DEBUG_STRATEGY) {
+        ALOGD("  - b=%s", vectorToString(outB, n).c_str());
+    }
 
     // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
     // SSerr is the residual sum of squares (variance of the error),
@@ -528,11 +532,11 @@
         sstot += w[h] * w[h] * var * var;
     }
     *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
-#if DEBUG_STRATEGY
-    ALOGD("  - sserr=%f", sserr);
-    ALOGD("  - sstot=%f", sstot);
-    ALOGD("  - det=%f", *outDet);
-#endif
+    if (DEBUG_STRATEGY) {
+        ALOGD("  - sserr=%f", sserr);
+        ALOGD("  - sstot=%f", sstot);
+        ALOGD("  - det=%f", *outDet);
+    }
     return true;
 }
 
@@ -655,13 +659,11 @@
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = degree;
             outEstimator->confidence = xdet * ydet;
-#if DEBUG_STRATEGY
-            ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
-                    int(outEstimator->degree),
-                    vectorToString(outEstimator->xCoeff, n).c_str(),
-                    vectorToString(outEstimator->yCoeff, n).c_str(),
-                    outEstimator->confidence);
-#endif
+            if (DEBUG_STRATEGY) {
+                ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+                      int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
+                      vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
+            }
             return true;
         }
     }
@@ -1169,9 +1171,9 @@
     outEstimator->time = newestMovement.eventTime;
     outEstimator->degree = 2; // similar results to 2nd degree fit
     outEstimator->confidence = 1;
-#if DEBUG_STRATEGY
-    ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
-#endif
+    if (DEBUG_STRATEGY) {
+        ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+    }
     return true;
 }
 
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 474a1e4..829bbdd 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -25,13 +25,6 @@
     // android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS.
     const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
 
-    // Compatibility changes.
-    /**
-      * TODO(b/157929241): remove this before closing the bug. This is needed temporarily
-      * to identify apps that are using this flag.
-      */
-    const long BLOCK_FLAG_SLIPPERY = 157929241;
-
     // Indicate invalid battery capacity
     const int INVALID_BATTERY_CAPACITY = -1;
 
@@ -53,4 +46,53 @@
      * set of flags, including in input/Input.h and in android/input.h.
      */
     const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
+
+    @Backing(type="int")
+    enum InputFeature {
+        /**
+         * Does not construct an input channel for this window.  The channel will therefore
+         * be incapable of receiving input.
+         */
+        NO_INPUT_CHANNEL = 0x00000002,
+
+        /**
+         * When this window has focus, does not call user activity for all input events so
+         * the application will have to do it itself.  Should only be used by
+         * the keyguard and phone app.
+         *
+         * Should only be used by the keyguard and phone app.
+         */
+        DISABLE_USER_ACTIVITY = 0x00000004,
+
+        /**
+         * Internal flag used to indicate that input should be dropped on this window.
+         */
+        DROP_INPUT = 0x00000008,
+
+        /**
+         * Internal flag used to indicate that input should be dropped on this window if this window
+         * is obscured.
+         */
+        DROP_INPUT_IF_OBSCURED = 0x00000010,
+
+        /**
+         * An input spy window. This window will receive all pointer events within its touchable
+         * area, but will will not stop events from being sent to other windows below it in z-order.
+         * An input event will be dispatched to all spy windows above the top non-spy window at the
+         * event's coordinates.
+         */
+        SPY = 0x00000020,
+
+        /**
+         * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
+         * to receive events from a stylus device within its touchable region. All other pointer
+         * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
+         *
+         * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
+         * not set.
+         *
+         * The window must be a trusted overlay to use this input feature.
+         */
+        INTERCEPTS_STYLUS = 0x00000040,
+    }
 }
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b1ef753..a92016b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -226,39 +226,23 @@
     static constexpr float Y_SCALE = 3.0;
     static constexpr float X_OFFSET = 1;
     static constexpr float Y_OFFSET = 1.1;
-
-    static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
+    static constexpr float RAW_X_SCALE = 4.0;
+    static constexpr float RAW_Y_SCALE = -5.0;
+    static constexpr float RAW_X_OFFSET = 12;
+    static constexpr float RAW_Y_OFFSET = -41.1;
 
     int32_t mId;
     ui::Transform mTransform;
-
-    void SetUp() override;
-    void TearDown() override;
+    ui::Transform mRawTransform;
 
     void initializeEventWithHistory(MotionEvent* event);
     void assertEqualsEventWithHistory(const MotionEvent* event);
 };
 
-const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
-        !base::GetProperty("persist.debug.per_window_input_rotation", "").empty()
-        ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false))
-        : std::nullopt;
-
-void MotionEventTest::SetUp() {
-    // Ensure per_window_input_rotation is enabled.
-    base::SetProperty("persist.debug.per_window_input_rotation", "true");
-}
-
-void MotionEventTest::TearDown() {
-    const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value()
-            ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false")
-            : "";
-    base::SetProperty("persist.debug.per_window_input_rotation", val);
-}
-
 void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
     mId = InputEvent::nextId();
     mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
+    mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
 
     PointerProperties pointerProperties[2];
     pointerProperties[0].clear();
@@ -294,9 +278,8 @@
                       AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
                       MotionClassification::NONE, mTransform, 2.0f, 2.1f,
                       AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                      ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
-                      ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
-                      pointerCoords);
+                      mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
+                      pointerProperties, pointerCoords);
 
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -373,39 +356,37 @@
     ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
     ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
 
-    ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
-            getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
-            getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
-            getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
-            getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(211, event->getRawPointerCoords(0)->
-            getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(221, event->getRawPointerCoords(1)->
-            getAxisValue(AMOTION_EVENT_AXIS_Y));
+    ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+    ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+    ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+    ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+    ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+    ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
 
-    ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
-    ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
-    ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
-    ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
-    ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
-    ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+    ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
+    ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
+    ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
+    ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+              event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
+    ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
+    ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
 
-    ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
-    ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
-    ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
-    ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
-    ASSERT_EQ(210, event->getRawX(0));
-    ASSERT_EQ(220, event->getRawX(1));
+    ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
+    ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
+    ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
+    ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
+    ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
+    ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
 
-    ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
-    ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
-    ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
-    ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
-    ASSERT_EQ(211, event->getRawY(0));
-    ASSERT_EQ(221, event->getRawY(1));
+    ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
+    ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
+    ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
+    ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
+    ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
+    ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
 
     ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
     ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
@@ -463,12 +444,19 @@
     ASSERT_EQ(217, event->getToolMinor(0));
     ASSERT_EQ(227, event->getToolMinor(1));
 
-    ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
-    ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
-    ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
-    ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
-    ASSERT_EQ(218, event->getOrientation(0));
-    ASSERT_EQ(228, event->getOrientation(1));
+    // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is "up",
+    // and the positive y direction is "down".
+    auto toScaledOrientation = [](float angle) {
+        const float x = sinf(angle) * X_SCALE;
+        const float y = -cosf(angle) * Y_SCALE;
+        return atan2f(x, -y);
+    };
+    ASSERT_EQ(toScaledOrientation(18), event->getHistoricalOrientation(0, 0));
+    ASSERT_EQ(toScaledOrientation(28), event->getHistoricalOrientation(1, 0));
+    ASSERT_EQ(toScaledOrientation(118), event->getHistoricalOrientation(0, 1));
+    ASSERT_EQ(toScaledOrientation(128), event->getHistoricalOrientation(1, 1));
+    ASSERT_EQ(toScaledOrientation(218), event->getOrientation(0));
+    ASSERT_EQ(toScaledOrientation(228), event->getOrientation(1));
 }
 
 TEST_F(MotionEventTest, Properties) {
@@ -537,14 +525,15 @@
 TEST_F(MotionEventTest, Scale) {
     MotionEvent event;
     initializeEventWithHistory(&event);
+    const float unscaledOrientation = event.getOrientation(0);
 
     event.scale(2.0f);
 
     ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
     ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
 
-    ASSERT_EQ(210 * 2, event.getRawX(0));
-    ASSERT_EQ(211 * 2, event.getRawY(0));
+    ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
+    ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
     ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
     ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
     ASSERT_EQ(212, event.getPressure(0));
@@ -553,7 +542,7 @@
     ASSERT_EQ(215 * 2, event.getTouchMinor(0));
     ASSERT_EQ(216 * 2, event.getToolMajor(0));
     ASSERT_EQ(217 * 2, event.getToolMinor(0));
-    ASSERT_EQ(218, event.getOrientation(0));
+    ASSERT_EQ(unscaledOrientation, event.getOrientation(0));
 }
 
 TEST_F(MotionEventTest, Parcel) {
@@ -592,10 +581,10 @@
     // The geometrical representation is irrelevant to the test, it's just easy to generate
     // and check rotation.  We set the orientation to the same angle.
     // Coordinate system: down is increasing Y, right is increasing X.
-    const float PI_180 = float(M_PI / 180);
-    const float RADIUS = 10;
-    const float ARC = 36;
-    const float ROTATION = ARC * 2;
+    static constexpr float PI_180 = float(M_PI / 180);
+    static constexpr float RADIUS = 10;
+    static constexpr float ARC = 36;
+    static constexpr float ROTATION = ARC * 2;
 
     const size_t pointerCount = 11;
     PointerProperties pointerProperties[pointerCount];
@@ -616,9 +605,8 @@
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                      MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
                      0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
-                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
-                     0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
-                     pointerCoords);
+                     identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
+                     pointerProperties, pointerCoords);
     float originalRawX = 0 + 3;
     float originalRawY = -RADIUS + 2;
 
@@ -659,9 +647,8 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
-MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
-                                 const ui::Transform& transform,
-                                 uint32_t displayOrientation = ui::Transform::ROT_0) {
+MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy,
+                              const ui::Transform& transform, const ui::Transform& rawTransform) {
     std::vector<PointerProperties> pointerProperties;
     pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
     std::vector<PointerCoords> pointerCoords;
@@ -672,24 +659,30 @@
     pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
     nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
     MotionEvent event;
-    event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
-                     /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+    event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
+                     /* displayId */ 0, INVALID_HMAC, action,
                      /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
                      /* buttonState */ 0, MotionClassification::NONE, transform,
                      /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation,
-                     /* displayWidth */ 400,
-                     /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
-                     pointerProperties.data(), pointerCoords.data());
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, rawTransform, eventTime, eventTime,
+                     pointerCoords.size(), pointerProperties.data(), pointerCoords.data());
     return event;
 }
 
+MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
+                                 const ui::Transform& transform,
+                                 const ui::Transform& rawTransform) {
+    return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy,
+                             transform, rawTransform);
+}
+
 TEST_F(MotionEventTest, ApplyTransform) {
     // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
     ui::Transform identity;
-    ui::Transform xform(ui::Transform::ROT_90, 800, 400);
-    xform.set(xform.tx() + 20, xform.ty() + 40);
-    MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
+    ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+    transform.set(transform.tx() + 20, transform.ty() + 40);
+    ui::Transform rawTransform(ui::Transform::ROT_90, 800, 400);
+    MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
     ASSERT_EQ(700, event.getRawX(0));
     ASSERT_EQ(60, event.getRawY(0));
     ASSERT_NE(event.getRawX(0), event.getX(0));
@@ -698,10 +691,10 @@
     ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
     ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
 
-    MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity);
-    const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
-                                        xform[0][1], xform[1][1], xform[2][1],
-                                        xform[0][2], xform[1][2], xform[2][2]};
+    MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity, identity);
+    const std::array<float, 9> rowMajor{transform[0][0], transform[1][0], transform[2][0],
+                                        transform[0][1], transform[1][1], transform[2][1],
+                                        transform[0][2], transform[1][2], transform[2][2]};
     changedEvent.applyTransform(rowMajor);
 
     // transformContent effectively rotates the raw coordinates, so those should now include
@@ -721,16 +714,39 @@
                 changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001);
 }
 
+TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
+    constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
+                                                                    AMOTION_EVENT_ACTION_DOWN),
+                                                          std::pair(AINPUT_SOURCE_JOYSTICK,
+                                                                    AMOTION_EVENT_ACTION_MOVE)};
+    // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+    ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+    transform.set(transform.tx() + 20, transform.ty() + 40);
+
+    for (const auto& [source, action] : kNonTransformedSources) {
+        const MotionEvent event =
+                createMotionEvent(source, action, 60, 100, 0, 0, transform, transform);
+
+        // These events should not be transformed in any way.
+        ASSERT_EQ(60, event.getX(0));
+        ASSERT_EQ(100, event.getY(0));
+        ASSERT_EQ(event.getRawX(0), event.getX(0));
+        ASSERT_EQ(event.getRawY(0), event.getY(0));
+    }
+}
+
 TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
-    constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL,
-                                                 AINPUT_SOURCE_MOUSE_RELATIVE,
-                                                 AINPUT_SOURCE_JOYSTICK};
-    for (uint32_t source : NON_POINTER_SOURCES) {
-        // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
-        ui::Transform xform(ui::Transform::ROT_90, 800, 400);
-        xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
-        event.setSource(source);
+    constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
+                                                                AMOTION_EVENT_ACTION_DOWN),
+                                                      std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+                                                                AMOTION_EVENT_ACTION_MOVE)};
+    // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+    ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+    transform.set(transform.tx() + 20, transform.ty() + 40);
+
+    for (const auto& [source, action] : kNonPointerSources) {
+        const MotionEvent event =
+                createMotionEvent(source, action, 60, 100, 42, 96, transform, transform);
 
         // Since this event comes from a non-pointer source, it should include rotation but not
         // translation/offset.
@@ -741,72 +757,34 @@
     }
 }
 
-TEST_F(MotionEventTest, RawCompatTransform) {
-    {
-        // Make sure raw is raw regardless of transform translation.
-        ui::Transform xform;
-        xform.set(20, 40);
-        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform);
-        ASSERT_EQ(60, event.getRawX(0));
-        ASSERT_EQ(100, event.getRawY(0));
-        ASSERT_NE(event.getRawX(0), event.getX(0));
-        ASSERT_NE(event.getRawY(0), event.getY(0));
-        // Relative values should not be modified.
-        ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
-        ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
-    }
+TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) {
+    const ui::Transform identity;
+    ui::Transform transform;
+    transform.set({1.1, -2.2, 3.3, -4.4, 5.5, -6.6, 0, 0, 1});
+    ui::Transform rawTransform;
+    rawTransform.set({-6.6, 5.5, -4.4, 3.3, -2.2, 1.1, 0, 0, 1});
+    auto transformWithoutTranslation = [](const ui::Transform& t, float x, float y) {
+        auto newPoint = t.transform(x, y);
+        auto newOrigin = t.transform(0, 0);
+        return newPoint - newOrigin;
+    };
 
-    // Next check that getRaw contains rotation (for compatibility) but otherwise is still
-    // "Screen-space". The following tests check all 3 rotations.
-    {
-        // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
-        ui::Transform xform(ui::Transform::ROT_90, 800, 400);
-        xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
-        ASSERT_EQ(700, event.getRawX(0));
-        ASSERT_EQ(60, event.getRawY(0));
-        ASSERT_NE(event.getRawX(0), event.getX(0));
-        ASSERT_NE(event.getRawY(0), event.getY(0));
-        // Relative values should be rotated but not translated.
-        ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
-        ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
-    }
+    const MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
 
-    {
-        // Same as above, but check rotate-180.
-        ui::Transform xform(ui::Transform::ROT_180, 400, 800);
-        xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180);
-        ASSERT_EQ(340, event.getRawX(0));
-        ASSERT_EQ(700, event.getRawY(0));
-        // Relative values should be rotated but not translated.
-        ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
-        ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
-    }
+    // The x and y axes should have the window transform applied.
+    const auto newPoint = transform.transform(60, 100);
+    ASSERT_EQ(newPoint.x, event.getX(0));
+    ASSERT_EQ(newPoint.y, event.getY(0));
 
-    {
-        // Same as above, but check rotate-270.
-        ui::Transform xform(ui::Transform::ROT_270, 800, 400);
-        xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270);
-        ASSERT_EQ(100, event.getRawX(0));
-        ASSERT_EQ(340, event.getRawY(0));
-        // Relative values should be rotated but not translated.
-        ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
-        ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
-    }
+    // The raw values should have the display transform applied.
+    const auto raw = rawTransform.transform(60, 100);
+    ASSERT_EQ(raw.x, event.getRawX(0));
+    ASSERT_EQ(raw.y, event.getRawY(0));
 
-    {
-        // Finally, check that raw isn't effected by transform
-        ui::Transform xform(ui::Transform::ROT_270, 800, 400);
-        xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
-        ASSERT_EQ(700, event.getRawX(0));
-        ASSERT_EQ(60, event.getRawY(0));
-        // Relative values should be rotated but not translated.
-        ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
-        ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
-    }
+    // Relative values should have the window transform applied without any translation.
+    const auto rel = transformWithoutTranslation(transform, 42, 96);
+    ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+    ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
 }
 
 TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -832,8 +810,7 @@
                          DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
                          identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                         INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
                          0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
     }
@@ -854,9 +831,9 @@
     event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
                      AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
-                     pointerCount, pointerProperties, pointerCoords);
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform,
+                     0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+                     pointerCoords);
     event.offsetLocation(20, 60);
     ASSERT_EQ(280, event.getRawXCursorPosition());
     ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 5d1f2c3..05bc0bc 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -56,6 +56,7 @@
     void PublishAndConsumeFocusEvent();
     void PublishAndConsumeCaptureEvent();
     void PublishAndConsumeDragEvent();
+    void PublishAndConsumeTouchModeEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -159,13 +160,14 @@
     constexpr float yScale = 3;
     constexpr float xOffset = -10;
     constexpr float yOffset = -20;
+    constexpr float rawXScale = 4;
+    constexpr float rawYScale = -5;
+    constexpr float rawXOffset = -11;
+    constexpr float rawYOffset = 42;
     constexpr float xPrecision = 0.25;
     constexpr float yPrecision = 0.5;
     constexpr float xCursorPosition = 1.3;
     constexpr float yCursorPosition = 50.6;
-    constexpr uint32_t displayOrientation = ui::Transform::ROT_0;
-    constexpr int32_t displayWidth = 1000;
-    constexpr int32_t displayHeight = 2000;
     constexpr nsecs_t downTime = 3;
     constexpr size_t pointerCount = 3;
     constexpr nsecs_t eventTime = 4;
@@ -191,12 +193,14 @@
 
     ui::Transform transform;
     transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
+    ui::Transform rawTransform;
+    rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1});
     status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
                                             actionButton, flags, edgeFlags, metaState, buttonState,
                                             classification, transform, xPrecision, yPrecision,
-                                            xCursorPosition, yCursorPosition, displayOrientation,
-                                            displayWidth, displayHeight, downTime, eventTime,
-                                            pointerCount, pointerProperties, pointerCoords);
+                                            xCursorPosition, yCursorPosition, rawTransform,
+                                            downTime, eventTime, pointerCount, pointerProperties,
+                                            pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
@@ -233,9 +237,7 @@
     EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
     EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
     EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
-    EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation());
-    EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
-    EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
+    EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
     EXPECT_EQ(downTime, motionEvent->getDownTime());
     EXPECT_EQ(eventTime, motionEvent->getEventTime());
     EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -246,28 +248,24 @@
         EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
         EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
 
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-                motionEvent->getRawX(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                motionEvent->getRawY(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
-                  motionEvent->getX(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
-                  motionEvent->getY(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                motionEvent->getPressure(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                motionEvent->getSize(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                motionEvent->getTouchMajor(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                motionEvent->getTouchMinor(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                motionEvent->getToolMajor(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                motionEvent->getToolMinor(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
-                motionEvent->getOrientation(i));
+        const auto& pc = pointerCoords[i];
+        EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
+        EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
+        EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
+        EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+        EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
+        EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
+        EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
+        EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i));
+        EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i));
+        EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i));
+
+        // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is
+        // "up", and the positive y direction is "down".
+        const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        const float x = sinf(unscaledOrientation) * xScale;
+        const float y = -cosf(unscaledOrientation) * yScale;
+        EXPECT_EQ(atan2f(x, -y), motionEvent->getOrientation(i));
     }
 
     status = mConsumer->sendFinishedSignal(seq, false);
@@ -292,10 +290,9 @@
     constexpr uint32_t seq = 15;
     int32_t eventId = InputEvent::nextId();
     constexpr bool hasFocus = true;
-    constexpr bool inTouchMode = true;
     const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
+    status = mPublisher->publishFocusEvent(seq, eventId, hasFocus);
     ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
 
     uint32_t consumeSeq;
@@ -311,7 +308,6 @@
     EXPECT_EQ(seq, consumeSeq);
     EXPECT_EQ(eventId, focusEvent->getId());
     EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
-    EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
 
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
@@ -413,6 +409,46 @@
             << "finished signal's consume time should be greater than publish time";
 }
 
+void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() {
+    status_t status;
+
+    constexpr uint32_t seq = 15;
+    int32_t eventId = InputEvent::nextId();
+    constexpr bool touchModeEnabled = true;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled);
+    ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+            << "consumer should have returned a touch mode event";
+
+    const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(eventId, touchModeEvent.getId());
+    EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode());
+
+    status = mConsumer->sendFinishedSignal(seq, true);
+    ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
+}
+
 TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
     const int32_t inputEventId = 20;
     std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
@@ -449,6 +485,10 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
 }
 
+TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
     status_t status;
     const size_t pointerCount = 1;
@@ -460,12 +500,12 @@
     }
 
     ui::Transform identityTransform;
-    status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
-                                            0, 0, 0, MotionClassification::NONE, identityTransform,
-                                            0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
-                                            pointerProperties, pointerCoords);
+    status =
+            mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+                                           0, 0, 0, MotionClassification::NONE, identityTransform,
+                                           0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                           AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+                                           0, 0, pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -477,12 +517,12 @@
     PointerCoords pointerCoords[pointerCount];
 
     ui::Transform identityTransform;
-    status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
-                                            0, 0, 0, MotionClassification::NONE, identityTransform,
-                                            0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
-                                            pointerProperties, pointerCoords);
+    status =
+            mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+                                           0, 0, 0, MotionClassification::NONE, identityTransform,
+                                           0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                           AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+                                           0, 0, pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -499,12 +539,12 @@
     }
 
     ui::Transform identityTransform;
-    status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
-                                            0, 0, 0, MotionClassification::NONE, identityTransform,
-                                            0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
-                                            pointerProperties, pointerCoords);
+    status =
+            mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+                                           0, 0, 0, MotionClassification::NONE, identityTransform,
+                                           0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                           AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+                                           0, 0, pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -520,6 +560,7 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
 }
 
 } // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 59fed1f..b6a9476 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -49,7 +49,7 @@
   CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
 
   CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
-  CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 4);
   CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
   CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -74,16 +74,17 @@
   CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
   CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
   CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
-  CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136);
-  CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140);
-  CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
+  CHECK_OFFSET(InputMessage::Body::Motion, dsdxRaw, 136);
+  CHECK_OFFSET(InputMessage::Body::Motion, dtdxRaw, 140);
+  CHECK_OFFSET(InputMessage::Body::Motion, dtdyRaw, 144);
+  CHECK_OFFSET(InputMessage::Body::Motion, dsdyRaw, 148);
+  CHECK_OFFSET(InputMessage::Body::Motion, txRaw, 152);
+  CHECK_OFFSET(InputMessage::Body::Motion, tyRaw, 156);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 160);
 
   CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
-  CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5);
-  CHECK_OFFSET(InputMessage::Body::Focus, empty, 6);
+  CHECK_OFFSET(InputMessage::Body::Focus, empty, 5);
 
   CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
@@ -102,6 +103,10 @@
   CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
   CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
+
+  CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4);
+  CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5);
 }
 
 void TestHeaderSize() {
@@ -123,6 +128,7 @@
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
     static_assert(sizeof(InputMessage::Body::Drag) == 16);
+    static_assert(sizeof(InputMessage::Body::TouchMode) == 8);
     // Timeline
     static_assert(GraphicsTimeline::SIZE == 2);
     static_assert(sizeof(InputMessage::Body::Timeline) == 24);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 13e2b02..a87b187 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -184,8 +184,7 @@
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                          MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
                          0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                         INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
                          entry.eventTime.count(), pointerCount, properties, coords);
 
         events.emplace_back(event);
@@ -344,7 +343,7 @@
         { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
-                            872.794617);
+                            764.345703);
     computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
                             951.698181);
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index b29c9a4..f2b59ea 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -43,12 +43,12 @@
 
     ui::Transform transform;
     transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
+    ui::Transform identity;
     event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                      MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/,
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/,
                      200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     return event;
 }
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 2dd6c4f..fc9680b 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -77,6 +77,7 @@
 struct FrameCallback {
     AChoreographer_frameCallback callback;
     AChoreographer_frameCallback64 callback64;
+    AChoreographer_extendedFrameCallback extendedCallback;
     void* data;
     nsecs_t dueTime;
 
@@ -95,6 +96,20 @@
 
 class Choreographer;
 
+/**
+ * Implementation of AChoreographerFrameCallbackData.
+ */
+struct ChoreographerFrameCallbackDataImpl {
+    int64_t frameTimeNanos{0};
+
+    std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength>
+            frameTimelines;
+
+    size_t preferredFrameTimelineIndex;
+
+    const Choreographer* choreographer;
+};
+
 struct {
     std::mutex lock;
     std::vector<Choreographer*> ptrs GUARDED_BY(lock);
@@ -107,7 +122,9 @@
 public:
     explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
     void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
-                                  AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+                                  AChoreographer_frameCallback64 cb64,
+                                  AChoreographer_extendedFrameCallback extendedCallback, void* data,
+                                  nsecs_t delay);
     void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
             EXCLUDES(gChoreographers.lock);
     void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
@@ -127,9 +144,8 @@
 
     static Choreographer* getForThread();
     virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
-    int64_t getVsyncId() const;
-    int64_t getFrameDeadline() const;
     int64_t getFrameInterval() const;
+    bool inCallback() const;
 
 private:
     Choreographer(const Choreographer&) = delete;
@@ -145,6 +161,8 @@
 
     void scheduleCallbacks();
 
+    ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+
     std::mutex mLock;
     // Protected by mLock
     std::priority_queue<FrameCallback> mFrameCallbacks;
@@ -152,6 +170,7 @@
 
     nsecs_t mLatestVsyncPeriod = -1;
     VsyncEventData mLastVsyncEventData;
+    bool mInCallback = false;
 
     const sp<Looper> mLooper;
     const std::thread::id mThreadId;
@@ -192,6 +211,7 @@
     // Only poke DisplayManagerGlobal to unregister if we previously registered
     // callbacks.
     if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+        gChoreographers.registeredToDisplayManager = false;
         JNIEnv* env = getJniEnv();
         if (env == nullptr) {
             ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
@@ -210,10 +230,12 @@
     }
 }
 
-void Choreographer::postFrameCallbackDelayed(
-        AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+                                             AChoreographer_frameCallback64 cb64,
+                                             AChoreographer_extendedFrameCallback extendedCallback,
+                                             void* data, nsecs_t delay) {
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    FrameCallback callback{cb, cb64, data, now + delay};
+    FrameCallback callback{cb, cb64, extendedCallback, data, now + delay};
     {
         std::lock_guard<std::mutex> _l{mLock};
         mFrameCallbacks.push(callback);
@@ -305,8 +327,9 @@
         // Fortunately, these events are small so sending packets across the
         // socket should be atomic across processes.
         DisplayEventReceiver::Event event;
-        event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
-                                                           PhysicalDisplayId(0), systemTime()};
+        event.header =
+                DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+                                                    PhysicalDisplayId::fromPort(0), systemTime()};
         injectEvent(event);
     }
 }
@@ -368,7 +391,15 @@
     }
     mLastVsyncEventData = vsyncEventData;
     for (const auto& cb : callbacks) {
-        if (cb.callback64 != nullptr) {
+        if (cb.extendedCallback != nullptr) {
+            const ChoreographerFrameCallbackDataImpl frameCallbackData =
+                    createFrameCallbackData(timestamp);
+            mInCallback = true;
+            cb.extendedCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+                                        &frameCallbackData),
+                                cb.data);
+            mInCallback = false;
+        } else if (cb.callback64 != nullptr) {
             cb.callback64(timestamp, cb.data);
         } else if (cb.callback != nullptr) {
             cb.callback(timestamp, cb.data);
@@ -377,8 +408,8 @@
 }
 
 void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
-    ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.",
-            this, to_string(displayId).c_str(), toString(connected));
+    ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
+          to_string(displayId).c_str(), toString(connected));
 }
 
 void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
@@ -397,30 +428,33 @@
 
 void Choreographer::handleMessage(const Message& message) {
     switch (message.what) {
-    case MSG_SCHEDULE_CALLBACKS:
-        scheduleCallbacks();
-        break;
-    case MSG_SCHEDULE_VSYNC:
-        scheduleVsync();
-        break;
-    case MSG_HANDLE_REFRESH_RATE_UPDATES:
-        handleRefreshRateUpdates();
-        break;
+        case MSG_SCHEDULE_CALLBACKS:
+            scheduleCallbacks();
+            break;
+        case MSG_SCHEDULE_VSYNC:
+            scheduleVsync();
+            break;
+        case MSG_HANDLE_REFRESH_RATE_UPDATES:
+            handleRefreshRateUpdates();
+            break;
     }
 }
 
-int64_t Choreographer::getVsyncId() const {
-    return mLastVsyncEventData.id;
-}
-
-int64_t Choreographer::getFrameDeadline() const {
-    return mLastVsyncEventData.deadlineTimestamp;
-}
-
 int64_t Choreographer::getFrameInterval() const {
     return mLastVsyncEventData.frameInterval;
 }
 
+bool Choreographer::inCallback() const {
+    return mInCallback;
+}
+
+ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
+    return {.frameTimeNanos = timestamp,
+            .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex,
+            .frameTimelines = mLastVsyncEventData.frameTimelines,
+            .choreographer = this};
+}
+
 } // namespace android
 using namespace android;
 
@@ -433,6 +467,12 @@
     return reinterpret_cast<const Choreographer*>(choreographer);
 }
 
+static inline const ChoreographerFrameCallbackDataImpl*
+AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(
+        const AChoreographerFrameCallbackData* data) {
+    return reinterpret_cast<const ChoreographerFrameCallbackDataImpl*>(data);
+}
+
 // Glue for private C api
 namespace android {
 void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
@@ -485,6 +525,11 @@
                                                     void* data, uint32_t delayMillis) {
     return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
 }
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+                                                   AChoreographer_extendedFrameCallback callback,
+                                                   void* data) {
+    return AChoreographer_postExtendedFrameCallback(choreographer, callback, data);
+}
 void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
                                                      AChoreographer_refreshRateCallback callback,
                                                      void* data) {
@@ -495,13 +540,29 @@
                                                        void* data) {
     return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
 }
-
-int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) {
-    return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+        const AChoreographerFrameCallbackData* data) {
+    return AChoreographerFrameCallbackData_getFrameTimeNanos(data);
 }
-
-int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) {
-    return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline();
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+        const AChoreographerFrameCallbackData* data) {
+    return AChoreographerFrameCallbackData_getFrameTimelinesLength(data);
+}
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+        const AChoreographerFrameCallbackData* data) {
+    return AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(data);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+        const AChoreographerFrameCallbackData* data, size_t index) {
+    return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+        const AChoreographerFrameCallbackData* data, size_t index) {
+    return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+        const AChoreographerFrameCallbackData* data, size_t index) {
+    return AChoreographerFrameCallbackData_getFrameTimelineDeadline(data, index);
 }
 
 int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) {
@@ -521,24 +582,32 @@
 }
 
 void AChoreographer_postFrameCallback(AChoreographer* choreographer,
-        AChoreographer_frameCallback callback, void* data) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            callback, nullptr, data, 0);
+                                      AChoreographer_frameCallback callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)
+            ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
 }
 void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
-        AChoreographer_frameCallback callback, void* data, long delayMillis) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            callback, nullptr, data, ms2ns(delayMillis));
+                                             AChoreographer_frameCallback callback, void* data,
+                                             long delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)
+            ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+                                              AChoreographer_extendedFrameCallback callback,
+                                              void* data) {
+    AChoreographer_to_Choreographer(choreographer)
+            ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
 }
 void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
-        AChoreographer_frameCallback64 callback, void* data) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            nullptr, callback, data, 0);
+                                        AChoreographer_frameCallback64 callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)
+            ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
 }
 void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
-        AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            nullptr, callback, data, ms2ns(delayMillis));
+                                               AChoreographer_frameCallback64 callback, void* data,
+                                               uint32_t delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)
+            ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
 }
 void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
                                                 AChoreographer_refreshRateCallback callback,
@@ -551,6 +620,58 @@
     AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
 }
 
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+        const AChoreographerFrameCallbackData* data) {
+    const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+            AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+    LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+                        "Data is only valid in callback");
+    return frameCallbackData->frameTimeNanos;
+}
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+        const AChoreographerFrameCallbackData* data) {
+    const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+            AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+    LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+                        "Data is only valid in callback");
+    return frameCallbackData->frameTimelines.size();
+}
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+        const AChoreographerFrameCallbackData* data) {
+    const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+            AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+    LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+                        "Data is only valid in callback");
+    return frameCallbackData->preferredFrameTimelineIndex;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+        const AChoreographerFrameCallbackData* data, size_t index) {
+    const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+            AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+    LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+                        "Data is only valid in callback");
+    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+    return frameCallbackData->frameTimelines[index].id;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+        const AChoreographerFrameCallbackData* data, size_t index) {
+    const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+            AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+    LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+                        "Data is only valid in callback");
+    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+    return frameCallbackData->frameTimelines[index].expectedPresentTime;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+        const AChoreographerFrameCallbackData* data, size_t index) {
+    const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+            AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+    LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+                        "Data is only valid in callback");
+    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+    return frameCallbackData->frameTimelines[index].deadlineTimestamp;
+}
+
 AChoreographer* AChoreographer_create() {
     Choreographer* choreographer = new Choreographer(nullptr);
     status_t result = choreographer->initialize();
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 7d25ce8..4aa7e69 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -29,19 +29,6 @@
 // for consumption by callbacks.
 void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
 
-// Returns the vsync id of the last frame callback. Client are expected to call
-// this function from their frame callback function to get the vsyncId and pass
-// it together with a buffer or transaction to the Surface Composer. Calling
-// this function from anywhere else will return an undefined value.
-int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer);
-
-// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback.
-// Client are expected to call this function from their frame callback function
-// to get the deadline and use it to know whether a frame is likely to miss
-// presentation. Calling this function from anywhere else will return an undefined
-// value.
-int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer);
-
 // Returns the current interval in ns between frames.
 // Client are expected to call this function from their frame callback function.
 // Calling this function from anywhere else will return an undefined value.
@@ -63,11 +50,26 @@
 void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
                                                     AChoreographer_frameCallback64 callback,
                                                     void* data, uint32_t delayMillis);
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+                                                   AChoreographer_extendedFrameCallback callback,
+                                                   void* data);
 void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
                                                      AChoreographer_refreshRateCallback callback,
                                                      void* data);
 void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
                                                        AChoreographer_refreshRateCallback callback,
                                                        void* data);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+        const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+        const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+        const AChoreographerFrameCallbackData* data);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+        const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+        const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+        const AChoreographerFrameCallbackData* data, size_t index);
 
 } // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
index 35ae3d2..6fd4b8f 100644
--- a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
+++ b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
@@ -42,7 +42,8 @@
     typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
 
     sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
-                                    bool* outQueueEmpty, SurfaceTexture& cb,
+                                    HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+                                    SurfaceTexture& cb,
                                     SurfaceTexture_createReleaseFence createFence,
                                     SurfaceTexture_fenceWait fenceWait,
                                     void* fencePassThroughHandle);
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 6eaa84e..0f119f3 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -272,10 +272,11 @@
     status_t attachToContext(uint32_t tex);
 
     sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
-                                    float* outTransformMatrix, bool* outQueueEmpty,
+                                    HdrMetadata* outHdrMetadata, float* outTransformMatrix,
+                                    uint32_t* outTransform, bool* outQueueEmpty,
                                     SurfaceTexture_createReleaseFence createFence,
                                     SurfaceTexture_fenceWait fenceWait,
-                                    void* fencePassThroughHandle);
+                                    void* fencePassThroughHandle, ARect* currentCrop);
 
     /**
      * takeConsumerOwnership attaches a SurfaceTexture that is currently in the
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 85fe42f..2987f3a 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -19,6 +19,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
+#include <android/hdr_metadata.h>
 #include <jni.h>
 #include <system/graphics.h>
 
@@ -82,12 +83,12 @@
  * The caller gets ownership of the buffer and need to release it with
  * AHardwareBuffer_release.
  */
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
-                                               android_dataspace* outDataspace,
-                                               float* outTransformMatrix, bool* outNewContent,
-                                               ASurfaceTexture_createReleaseFence createFence,
-                                               ASurfaceTexture_fenceWait fenceWait,
-                                               void* fencePassThroughHandle);
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+        ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+        AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+        android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+        bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+        ASurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle, ARect* currentCrop);
 
 } // namespace android
 
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 9ed4915..4dbfde8 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -7,6 +7,13 @@
     AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
     AChoreographer_registerRefreshRateCallback; # apex # introduced=30
     AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
+    AChoreographer_postExtendedFrameCallback; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimeNanos; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33
+    AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineDeadline; # apex # introduced=33
     AChoreographer_create; # apex # introduced=30
     AChoreographer_destroy; # apex # introduced=30
     AChoreographer_getFd; # apex # introduced=30
@@ -28,9 +35,14 @@
       android::AChoreographer_routePostFrameCallbackDelayed64*;
       android::AChoreographer_routeRegisterRefreshRateCallback*;
       android::AChoreographer_routeUnregisterRefreshRateCallback*;
+      android::AChoreographer_routePostExtendedFrameCallback*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimeNanos*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimelinesLength*;
+      android::AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline*;
       android::AChoreographer_signalRefreshRateCallbacks*;
-      android::AChoreographer_getVsyncId*;
-      android::AChoreographer_getFrameDeadline*;
       android::AChoreographer_getFrameInterval*;
       android::ADisplay_acquirePhysicalDisplays*;
       android::ADisplay_release*;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 365e788..cf16739 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -28,7 +28,8 @@
 }
 
 sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
-                                               bool* outQueueEmpty, SurfaceTexture& st,
+                                               HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+                                               SurfaceTexture& st,
                                                SurfaceTexture_createReleaseFence createFence,
                                                SurfaceTexture_fenceWait fenceWait,
                                                void* fencePassThroughHandle) {
@@ -121,6 +122,7 @@
     st.computeCurrentTransformMatrixLocked();
 
     *outDataspace = item.mDataSpace;
+    *outHdrMetadata = item.mHdrMetadata;
     *outSlotid = slot;
     return st.mSlots[slot].mGraphicBuffer;
 }
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 62db6d0..d3d4cba 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -464,10 +464,12 @@
 }
 
 sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
-                                                float* outTransformMatrix, bool* outQueueEmpty,
+                                                HdrMetadata* outHdrMetadata,
+                                                float* outTransformMatrix, uint32_t* outTransform,
+                                                bool* outQueueEmpty,
                                                 SurfaceTexture_createReleaseFence createFence,
                                                 SurfaceTexture_fenceWait fenceWait,
-                                                void* fencePassThroughHandle) {
+                                                void* fencePassThroughHandle, ARect* currentCrop) {
     Mutex::Autolock _l(mMutex);
     sp<GraphicBuffer> buffer;
 
@@ -481,9 +483,11 @@
         return buffer;
     }
 
-    buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
-                                          createFence, fenceWait, fencePassThroughHandle);
+    buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outHdrMetadata, outQueueEmpty,
+                                          *this, createFence, fenceWait, fencePassThroughHandle);
     memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+    *outTransform = mCurrentTransform;
+    *currentCrop = mCurrentCrop;
     return buffer;
 }
 
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index c214ab7..39a925f 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -192,17 +192,23 @@
     texture->consumer->releaseConsumerOwnership();
 }
 
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
-                                               android_dataspace* outDataspace,
-                                               float* outTransformMatrix, bool* outNewContent,
-                                               ASurfaceTexture_createReleaseFence createFence,
-                                               ASurfaceTexture_fenceWait fenceWait, void* handle) {
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+        ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+        AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+        android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+        bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+        ASurfaceTexture_fenceWait fenceWait, void* handle, ARect* currentCrop) {
     sp<GraphicBuffer> buffer;
     *outNewContent = false;
     bool queueEmpty;
     do {
-        buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
-                                             &queueEmpty, createFence, fenceWait, handle);
+        HdrMetadata metadata;
+        buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, &metadata, outTransformMatrix,
+                                             outTransform, &queueEmpty, createFence, fenceWait,
+                                             handle, currentCrop);
+        *outHdrType = static_cast<AHdrMetadataType>(metadata.validTypes);
+        *outCta861_3 = metadata.cta8613;
+        *outSmpte2086 = metadata.smpte2086;
         if (!queueEmpty) {
             *outNewContent = true;
         }
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e2f32e3..cb3361b 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -30,7 +30,7 @@
 
 #include <private/android/AHardwareBufferHelpers.h>
 #include <android/hardware/graphics/common/1.1/types.h>
-
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 
 static constexpr int kFdBufferSize = 128 * sizeof(int);  // 128 ints
 
@@ -370,7 +370,7 @@
     if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
 
     bool supported = false;
-    GraphicBuffer* gBuffer = new GraphicBuffer();
+    sp<GraphicBuffer> gBuffer(new GraphicBuffer());
     status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers,
                                         desc->usage, &supported);
 
@@ -588,8 +588,12 @@
             "HAL and AHardwareBuffer pixel format don't match");
     static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
             "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
+                          AHARDWAREBUFFER_FORMAT_R8_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
 
     switch (format) {
+        case AHARDWAREBUFFER_FORMAT_R8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
         case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
@@ -641,6 +645,8 @@
 
 uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
   switch (format) {
+      case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+          return 1;
       case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
       case AHARDWAREBUFFER_FORMAT_D16_UNORM:
           return 2;
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 75f2385..18a4b2d 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -133,16 +133,51 @@
 
 int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) {
     static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
-    static_assert(static_cast<int>(ADATASPACE_SCRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SCRGB_LINEAR));
+    static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
+    static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
+    static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
+    static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
+    static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
+    static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
+    static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
+    static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
+    static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
+    static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
+    static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
+    static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
+    static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
+    static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
+    static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
+    static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
+    static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
+    static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
+    static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
+    static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
+    static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK));
+    static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
+    static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL));
+    static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
+    static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
     static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
     static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
     static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
     static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+    static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_PQ) ==
+        static_cast<int>(HAL_DATASPACE_BT2020_ITU_PQ));
     static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
+    static_assert(static_cast<int>(ADATASPACE_JFIF) == static_cast<int>(HAL_DATASPACE_V0_JFIF));
+    static_assert(static_cast<int>(ADATASPACE_BT601_625) == static_cast<int>(HAL_DATASPACE_V0_BT601_625));
+    static_assert(static_cast<int>(ADATASPACE_BT601_525) == static_cast<int>(HAL_DATASPACE_V0_BT601_525));
     static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020));
     static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
     static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
     static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
+    static_assert(static_cast<int>(ADATASPACE_BT2020_HLG) ==
+        static_cast<int>(HAL_DATASPACE_BT2020_HLG));
+    static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_HLG) ==
+        static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG));
+    static_assert(static_cast<int>(DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH));
+    static_assert(static_cast<int>(DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH));
 
     if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
             !isDataSpaceValid(window, dataSpace)) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index e759513..771844f 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -15,6 +15,13 @@
  */
 
 /**
+ * @defgroup ADataSpace Data Space
+ *
+ * ADataSpace describes how to interpret colors.
+ * @{
+ */
+
+/**
  * @file data_space.h
  */
 
@@ -43,6 +50,340 @@
     ADATASPACE_UNKNOWN = 0,
 
     /**
+     * Color-description aspects
+     *
+     * The following aspects define various characteristics of the color
+     * specification. These represent bitfields, so that a data space value
+     * can specify each of them independently.
+     */
+
+    /**
+     * Standard aspect
+     *
+     * Defines the chromaticity coordinates of the source primaries in terms of
+     * the CIE 1931 definition of x and y specified in ISO 11664-1.
+     */
+    STANDARD_MASK = 63 << 16,
+
+    /**
+     * Chromacity coordinates are unknown or are determined by the application.
+     * Implementations shall use the following suggested standards:
+     *
+     * All YCbCr formats: BT709 if size is 720p or larger (since most video
+     *                    content is letterboxed this corresponds to width is
+     *                    1280 or greater, or height is 720 or greater).
+     *                    BT601_625 if size is smaller than 720p or is JPEG.
+     * All RGB formats:   BT709.
+     *
+     * For all other formats standard is undefined, and implementations should use
+     * an appropriate standard for the data represented.
+     */
+    STANDARD_UNSPECIFIED = 0 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.300   0.600
+     *  blue            0.150   0.060
+     *  red             0.640   0.330
+     *  white (D65)     0.3127  0.3290
+     *
+     * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
+     * for RGB conversion.
+     */
+    STANDARD_BT709 = 1 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.290   0.600
+     *  blue            0.150   0.060
+     *  red             0.640   0.330
+     *  white (D65)     0.3127  0.3290
+     *
+     *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+     *  for RGB conversion from the one purely determined by the primaries
+     *  to minimize the color shift into RGB space that uses BT.709
+     *  primaries.
+     */
+    STANDARD_BT601_625 = 2 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.290   0.600
+     *  blue            0.150   0.060
+     *  red             0.640   0.330
+     *  white (D65)     0.3127  0.3290
+     *
+     * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
+     * for RGB conversion.
+     */
+    STANDARD_BT601_625_UNADJUSTED = 3 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.310   0.595
+     *  blue            0.155   0.070
+     *  red             0.630   0.340
+     *  white (D65)     0.3127  0.3290
+     *
+     *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+     *  for RGB conversion from the one purely determined by the primaries
+     *  to minimize the color shift into RGB space that uses BT.709
+     *  primaries.
+     */
+    STANDARD_BT601_525 = 4 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.310   0.595
+     *  blue            0.155   0.070
+     *  red             0.630   0.340
+     *  white (D65)     0.3127  0.3290
+     *
+     * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
+     * for RGB conversion (as in SMPTE 240M).
+     */
+    STANDARD_BT601_525_UNADJUSTED = 5 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.170   0.797
+     *  blue            0.131   0.046
+     *  red             0.708   0.292
+     *  white (D65)     0.3127  0.3290
+     *
+     * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+     * for RGB conversion.
+     */
+    STANDARD_BT2020 = 6 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.170   0.797
+     *  blue            0.131   0.046
+     *  red             0.708   0.292
+     *  white (D65)     0.3127  0.3290
+     *
+     * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+     * for RGB conversion using the linear domain.
+     */
+    STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
+
+    /**
+     * Primaries:       x      y
+     *  green           0.21   0.71
+     *  blue            0.14   0.08
+     *  red             0.67   0.33
+     *  white (C)       0.310  0.316
+     *
+     * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
+     * for RGB conversion.
+     */
+    STANDARD_BT470M = 8 << 16,
+
+    /**
+     * Primaries:       x       y
+     *  green           0.243   0.692
+     *  blue            0.145   0.049
+     *  red             0.681   0.319
+     *  white (C)       0.310   0.316
+     *
+     * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
+     * for RGB conversion.
+     */
+    STANDARD_FILM = 9 << 16,
+
+    /**
+     * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
+     * Primaries:       x       y
+     *  green           0.265   0.690
+     *  blue            0.150   0.060
+     *  red             0.680   0.320
+     *  white (D65)     0.3127  0.3290
+     */
+    STANDARD_DCI_P3 = 10 << 16,
+
+    /**
+     * Adobe RGB
+     * Primaries:       x       y
+     *  green           0.210   0.710
+     *  blue            0.150   0.060
+     *  red             0.640   0.330
+     *  white (D65)     0.3127  0.3290
+     */
+    STANDARD_ADOBE_RGB = 11 << 16,
+
+    /**
+     * Transfer aspect
+     *
+     * Transfer characteristics are the opto-electronic transfer characteristic
+     * at the source as a function of linear optical intensity (luminance).
+     *
+     * For digital signals, E corresponds to the recorded value. Normally, the
+     * transfer function is applied in RGB space to each of the R, G and B
+     * components independently. This may result in color shift that can be
+     * minized by applying the transfer function in Lab space only for the L
+     * component. Implementation may apply the transfer function in RGB space
+     * for all pixel formats if desired.
+     */
+    TRANSFER_MASK = 31 << 22,
+
+    /**
+     * Transfer characteristics are unknown or are determined by the
+     * application.
+     *
+     * Implementations should use the following transfer functions:
+     *
+     * For YCbCr formats: use TRANSFER_SMPTE_170M
+     * For RGB formats: use TRANSFER_SRGB
+     *
+     * For all other formats transfer function is undefined, and implementations
+     * should use an appropriate standard for the data represented.
+     */
+    TRANSFER_UNSPECIFIED = 0 << 22,
+
+    /**
+     * Transfer characteristic curve:
+     *  E = L
+     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *      E - corresponding electrical signal
+     */
+    TRANSFER_LINEAR = 1 << 22,
+
+    /**
+     * Transfer characteristic curve:
+     *
+     * E = 1.055 * L^(1/2.4) - 0.055  for 0.0031308 <= L <= 1
+     *   = 12.92 * L                  for 0 <= L < 0.0031308
+     *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *     E - corresponding electrical signal
+     */
+    TRANSFER_SRGB = 2 << 22,
+
+    /**
+     * BT.601 525, BT.601 625, BT.709, BT.2020
+     *
+     * Transfer characteristic curve:
+     *  E = 1.099 * L ^ 0.45 - 0.099  for 0.018 <= L <= 1
+     *    = 4.500 * L                 for 0 <= L < 0.018
+     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *      E - corresponding electrical signal
+     */
+    TRANSFER_SMPTE_170M = 3 << 22,
+
+    /**
+     * Assumed display gamma 2.2.
+     *
+     * Transfer characteristic curve:
+     *  E = L ^ (1/2.2)
+     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *      E - corresponding electrical signal
+     */
+    TRANSFER_GAMMA2_2 = 4 << 22,
+
+    /**
+     *  display gamma 2.6.
+     *
+     * Transfer characteristic curve:
+     *  E = L ^ (1/2.6)
+     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *      E - corresponding electrical signal
+     */
+    TRANSFER_GAMMA2_6 = 5 << 22,
+
+    /**
+     *  display gamma 2.8.
+     *
+     * Transfer characteristic curve:
+     *  E = L ^ (1/2.8)
+     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *      E - corresponding electrical signal
+     */
+    TRANSFER_GAMMA2_8 = 6 << 22,
+
+    /**
+     * SMPTE ST 2084 (Dolby Perceptual Quantizer)
+     *
+     * Transfer characteristic curve:
+     *  E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+     *  c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+     *  c2 = 32 * 2413 / 4096 = 18.8515625
+     *  c3 = 32 * 2392 / 4096 = 18.6875
+     *  m = 128 * 2523 / 4096 = 78.84375
+     *  n = 0.25 * 2610 / 4096 = 0.1593017578125
+     *      L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+     *          L = 1 corresponds to 10000 cd/m2
+     *      E - corresponding electrical signal
+     */
+    TRANSFER_ST2084 = 7 << 22,
+
+    /**
+     * ARIB STD-B67 Hybrid Log Gamma
+     *
+     * Transfer characteristic curve:
+     *  E = r * L^0.5                 for 0 <= L <= 1
+     *    = a * ln(L - b) + c         for 1 < L
+     *  a = 0.17883277
+     *  b = 0.28466892
+     *  c = 0.55991073
+     *  r = 0.5
+     *      L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
+     *          to reference white level of 100 cd/m2
+     *      E - corresponding electrical signal
+     */
+    TRANSFER_HLG = 8 << 22,
+
+    /**
+     * Range aspect
+     *
+     * Defines the range of values corresponding to the unit range of 0-1.
+     * This is defined for YCbCr only, but can be expanded to RGB space.
+     */
+    RANGE_MASK = 7 << 27,
+
+    /**
+     * Range is unknown or are determined by the application.  Implementations
+     * shall use the following suggested ranges:
+     *
+     * All YCbCr formats: limited range.
+     * All RGB or RGBA formats (including RAW and Bayer): full range.
+     * All Y formats: full range
+     *
+     * For all other formats range is undefined, and implementations should use
+     * an appropriate range for the data represented.
+     */
+    RANGE_UNSPECIFIED = 0 << 27,
+
+    /**
+     * Full range uses all values for Y, Cb and Cr from
+     * 0 to 2^b-1, where b is the bit depth of the color format.
+     */
+    RANGE_FULL = 1 << 27,
+
+    /**
+     * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
+     * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of
+     * the color format.
+     *
+     * E.g. For 8-bit-depth formats:
+     * Luma (Y) samples should range from 16 to 235, inclusive
+     * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+     *
+     * For 10-bit-depth formats:
+     * Luma (Y) samples should range from 64 to 940, inclusive
+     * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+     */
+    RANGE_LIMITED = 2 << 27,
+
+    /**
+     * Extended range is used for scRGB. Intended for use with
+     * floating point pixel formats. [0.0 - 1.0] is the standard
+     * sRGB space. Values outside the range 0.0 - 1.0 can encode
+     * color outside the sRGB gamut.
+     * Used to blend / merge multiple dataspaces on a single display.
+     */
+    RANGE_EXTENDED = 3 << 27,
+
+    /**
      * scRGB linear encoding:
      *
      * The red, green, and blue components are stored in extended sRGB space,
@@ -103,6 +444,15 @@
     ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
 
     /**
+     * ITU-R Recommendation 2020 (BT.2020)
+     *
+     * Ultra High-definition television
+     *
+     * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
+     */
+    ADATASPACE_BT2020_ITU_PQ = 298188800,  // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+
+    /**
      * Adobe RGB
      *
      * Use full range, gamma 2.2 transfer and Adobe RGB primaries
@@ -112,6 +462,33 @@
     ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
 
     /**
+     * JPEG File Interchange Format (JFIF)
+     *
+     * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
+     *
+     * Use full range, SMPTE 170M transfer and BT.601_625 standard.
+     */
+    ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
+
+    /**
+     * ITU-R Recommendation 601 (BT.601) - 525-line
+     *
+     * Standard-definition television, 525 Lines (NTSC)
+     *
+     * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
+     */
+    ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+    /**
+     * ITU-R Recommendation 709 (BT.709)
+     *
+     * High-definition television
+     *
+     * Use limited range, SMPTE 170M transfer and BT.709 standard.
+     */
+    ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+    /**
      * ITU-R Recommendation 2020 (BT.2020)
      *
      * Ultra High-definition television
@@ -151,8 +528,38 @@
      * components.
      */
     ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
+
+    /**
+     * Hybrid Log Gamma encoding:
+     *
+     * Use full range, hybrid log gamma transfer and BT2020 standard.
+     */
+    ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
+
+    /**
+     * ITU Hybrid Log Gamma encoding:
+     *
+     * Use limited range, hybrid log gamma transfer and BT2020 standard.
+     */
+    ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
+
+    /**
+     * Depth:
+     *
+     * This value is valid with formats HAL_PIXEL_FORMAT_Y16 and HAL_PIXEL_FORMAT_BLOB.
+     */
+    DEPTH = 4096,
+
+    /**
+     * ISO 16684-1:2011(E) Dynamic Depth:
+     *
+     * Embedded depth metadata following the dynamic depth specification.
+     */
+    DYNAMIC_DEPTH = 4098
 };
 
 __END_DECLS
 
 #endif // ANDROID_DATA_SPACE_H
+
+/** @} */
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d93a84c..6f1f04d 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -158,6 +158,13 @@
      * cube-maps or multi-layered textures.
      */
     AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420             = 0x23,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R8_UNORM
+     *   OpenGL ES: GR_GL_R8
+     */
+    AHARDWAREBUFFER_FORMAT_R8_UNORM                 = 0x38,
 };
 
 /**
@@ -556,6 +563,7 @@
                                    int32_t* _Nonnull outBytesPerPixel,
                                    int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29);
 
+
 /**
  * Get the system wide unique id for an AHardwareBuffer.
  *
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7f01135..a319769 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1025,10 +1025,11 @@
 }
 
 static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
-                                                         int64_t frameTimelineVsyncId,
-                                                         int32_t inputEventId) {
-    return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO,
-                           frameTimelineVsyncId, inputEventId);
+                                                        int64_t frameTimelineVsyncId,
+                                                        int32_t inputEventId,
+                                                        int64_t startTimeNanos) {
+    return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
+                           inputEventId, startTimeNanos);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1089,10 +1090,13 @@
 /**
  * Retrieves an identifier for the next frame to be queued by this window.
  *
- * \return the next frame id.
+ * Frame ids start at 1 and are incremented on each new frame until the underlying surface changes,
+ * in which case the frame id is reset to 1.
+ *
+ * \return the next frame id (0 being uninitialized).
  */
-static inline int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
-    int64_t value;
+static inline uint64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+    uint64_t value;
     window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, &value);
     return value;
 }
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 570c7bc..07c5dd8 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -40,6 +40,11 @@
         "libui",
         "libutils",
     ],
+
+    static_libs: [
+        "libshaders",
+        "libtonemap",
+    ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
@@ -94,8 +99,10 @@
         "skia/debug/SkiaCapture.cpp",
         "skia/debug/SkiaMemoryReporter.cpp",
         "skia/filters/BlurFilter.cpp",
+        "skia/filters/GaussianBlurFilter.cpp",
+        "skia/filters/KawaseBlurFilter.cpp",
         "skia/filters/LinearEffect.cpp",
-        "skia/filters/StretchShaderFactory.cpp"
+        "skia/filters/StretchShaderFactory.cpp",
     ],
 }
 
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 9e466b6..c7ad058 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -26,23 +26,7 @@
 namespace android {
 namespace renderengine {
 
-std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) {
-    // Keep the ability to override by PROPERTIES:
-    char prop[PROPERTY_VALUE_MAX];
-    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
-    if (strcmp(prop, "gles") == 0) {
-        args.renderEngineType = RenderEngineType::GLES;
-    }
-    if (strcmp(prop, "threaded") == 0) {
-        args.renderEngineType = RenderEngineType::THREADED;
-    }
-    if (strcmp(prop, "skiagl") == 0) {
-        args.renderEngineType = RenderEngineType::SKIA_GL;
-    }
-    if (strcmp(prop, "skiaglthreaded") == 0) {
-        args.renderEngineType = RenderEngineType::SKIA_GL_THREADED;
-    }
-
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
     switch (args.renderEngineType) {
         case RenderEngineType::THREADED:
             ALOGD("Threaded RenderEngine with GLES Backend");
@@ -79,5 +63,16 @@
                         "output buffer not gpu writeable");
 }
 
+std::future<RenderEngineResult> RenderEngine::drawLayers(
+        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
+    const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
+    std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+    drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache,
+                       std::move(bufferFence));
+    return resultFuture;
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
new file mode 100644
index 0000000..471159f
--- /dev/null
+++ b/libs/renderengine/benchmark/Android.bp
@@ -0,0 +1,59 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_benchmark {
+    name: "librenderengine_bench",
+    defaults: [
+        "skia_deps",
+        "surfaceflinger_defaults",
+    ],
+    srcs: [
+        "main.cpp",
+        "Codec.cpp",
+        "Flags.cpp",
+        "RenderEngineBench.cpp",
+    ],
+    static_libs: [
+        "librenderengine",
+        "libshaders",
+        "libtonemap",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"RenderEngineBench\"",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libjnigraphics",
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "libprocessgroup",
+        "libsync",
+        "libui",
+        "libutils",
+    ],
+
+    data: ["resources/*"],
+}
diff --git a/libs/renderengine/benchmark/Codec.cpp b/libs/renderengine/benchmark/Codec.cpp
new file mode 100644
index 0000000..80e4fc4
--- /dev/null
+++ b/libs/renderengine/benchmark/Codec.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <RenderEngineBench.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+using namespace android;
+using namespace android::renderengine;
+
+namespace {
+struct DecoderDeleter {
+    void operator()(AImageDecoder* decoder) { AImageDecoder_delete(decoder); }
+};
+
+using AutoDecoderDeleter = std::unique_ptr<AImageDecoder, DecoderDeleter>;
+
+bool ok(int aImageDecoderResult, const char* path, const char* method) {
+    if (aImageDecoderResult == ANDROID_IMAGE_DECODER_SUCCESS) {
+        return true;
+    }
+
+    ALOGE("Failed AImageDecoder_%s on '%s' with error '%s'", method, path,
+          AImageDecoder_resultToString(aImageDecoderResult));
+    return false;
+}
+} // namespace
+
+namespace renderenginebench {
+
+void decode(const char* path, const sp<GraphicBuffer>& buffer) {
+    base::unique_fd fd{open(path, O_RDONLY)};
+    if (fd.get() < 0) {
+        ALOGE("Failed to open %s", path);
+        return;
+    }
+
+    AImageDecoder* decoder{nullptr};
+    auto result = AImageDecoder_createFromFd(fd.get(), &decoder);
+    if (!ok(result, path, "createFromFd")) {
+        return;
+    }
+
+    AutoDecoderDeleter deleter(decoder);
+
+    LOG_ALWAYS_FATAL_IF(buffer->getWidth() <= 0 || buffer->getHeight() <= 0,
+                        "Impossible buffer size!");
+    auto width = static_cast<int32_t>(buffer->getWidth());
+    auto height = static_cast<int32_t>(buffer->getHeight());
+    result = AImageDecoder_setTargetSize(decoder, width, height);
+    if (!ok(result, path, "setTargetSize")) {
+        return;
+    }
+
+    void* pixels{nullptr};
+    int32_t stride{0};
+    if (auto status = buffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &pixels,
+                                   nullptr /*outBytesPerPixel*/, &stride);
+        status < 0) {
+        ALOGE("Failed to lock pixels!");
+        return;
+    }
+
+    result = AImageDecoder_decodeImage(decoder, pixels, static_cast<size_t>(stride),
+                                       static_cast<size_t>(stride * height));
+    if (auto status = buffer->unlock(); status < 0) {
+        ALOGE("Failed to unlock pixels!");
+    }
+
+    // For the side effect of logging.
+    (void)ok(result, path, "decodeImage");
+}
+
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer) {
+    base::unique_fd fd{open(path, O_WRONLY | O_CREAT, S_IWUSR)};
+    if (fd.get() < 0) {
+        ALOGE("Failed to open %s", path);
+        return;
+    }
+
+    void* pixels{nullptr};
+    int32_t stride{0};
+    if (auto status = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels,
+                                   nullptr /*outBytesPerPixel*/, &stride);
+        status < 0) {
+        ALOGE("Failed to lock pixels!");
+        return;
+    }
+
+    AndroidBitmapInfo info{
+            .width = buffer->getWidth(),
+            .height = buffer->getHeight(),
+            .stride = static_cast<uint32_t>(stride),
+            .format = ANDROID_BITMAP_FORMAT_RGBA_8888,
+            .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE,
+    };
+    int result = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels,
+                                        ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, 80, &fd,
+                                        [](void* fdPtr, const void* data, size_t size) -> bool {
+                                            const ssize_t bytesWritten =
+                                                    write(reinterpret_cast<base::unique_fd*>(fdPtr)
+                                                                  ->get(),
+                                                          data, size);
+                                            return bytesWritten > 0 &&
+                                                    static_cast<size_t>(bytesWritten) == size;
+                                        });
+    if (result == ANDROID_BITMAP_RESULT_SUCCESS) {
+        ALOGD("Successfully encoded to '%s'", path);
+    } else {
+        ALOGE("Failed to encode to %s with error %d", path, result);
+    }
+
+    if (auto status = buffer->unlock(); status < 0) {
+        ALOGE("Failed to unlock pixels!");
+    }
+}
+
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/Flags.cpp b/libs/renderengine/benchmark/Flags.cpp
new file mode 100644
index 0000000..c5d5156
--- /dev/null
+++ b/libs/renderengine/benchmark/Flags.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include <stdio.h>
+#include <string.h>
+
+namespace {
+bool gSave = false;
+}
+
+namespace renderenginebench {
+
+void parseFlagsForHelp(int argc, char** argv) {
+    for (int i = 0; i < argc; i++) {
+        if (!strcmp(argv[i], "--help")) {
+            printf("RenderEngineBench-specific flags:\n");
+            printf("[--save]: Save the output to the device to confirm drawing result.\n");
+            break;
+        }
+    }
+}
+
+void parseFlags(int argc, char** argv) {
+    for (int i = 0; i < argc; i++) {
+        if (!strcmp(argv[i], "--save")) {
+            gSave = true;
+        }
+    }
+}
+
+bool save() {
+    return gSave;
+}
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
new file mode 100644
index 0000000..6c8f8e8
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <RenderEngineBench.h>
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/RenderEngine.h>
+
+#include <mutex>
+
+using namespace android;
+using namespace android::renderengine;
+
+///////////////////////////////////////////////////////////////////////////////
+//  Helpers for Benchmark::Apply
+///////////////////////////////////////////////////////////////////////////////
+
+std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) {
+    switch (type) {
+        case RenderEngine::RenderEngineType::SKIA_GL_THREADED:
+            return "skiaglthreaded";
+        case RenderEngine::RenderEngineType::SKIA_GL:
+            return "skiagl";
+        case RenderEngine::RenderEngineType::GLES:
+        case RenderEngine::RenderEngineType::THREADED:
+            LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
+            return "unused";
+    }
+}
+
+/**
+ * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a
+ * Benchmark which specifies which RenderEngineType it uses.
+ *
+ * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make
+ * it obvious which version is being run.
+ *
+ * @param b The benchmark family
+ * @param type The type of RenderEngine to use.
+ */
+static void AddRenderEngineType(benchmark::internal::Benchmark* b,
+                                RenderEngine::RenderEngineType type) {
+    b->Arg(static_cast<int64_t>(type));
+    b->ArgName(RenderEngineTypeName(type));
+}
+
+/**
+ * Run a benchmark once using SKIA_GL_THREADED.
+ */
+static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) {
+    AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//  Helpers for calling drawLayers
+///////////////////////////////////////////////////////////////////////////////
+
+std::pair<uint32_t, uint32_t> getDisplaySize() {
+    // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed
+    // to GraphicBuffer, which wants uint32_t.
+    static uint32_t width, height;
+    std::once_flag once;
+    std::call_once(once, []() {
+        auto surfaceComposerClient = SurfaceComposerClient::getDefault();
+        auto displayToken = surfaceComposerClient->getInternalDisplayToken();
+        ui::DisplayMode displayMode;
+        if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+            LOG_ALWAYS_FATAL("Failed to get active display mode!");
+        }
+        auto w = displayMode.resolution.width;
+        auto h = displayMode.resolution.height;
+        LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!");
+        width = static_cast<uint32_t>(w);
+        height = static_cast<uint32_t>(h);
+    });
+    return std::pair<uint32_t, uint32_t>(width, height);
+}
+
+// This value doesn't matter, as it's not read. TODO(b/199918329): Once we remove
+// GLESRenderEngine we can remove this, too.
+static constexpr const bool kUseFrameBufferCache = false;
+
+static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) {
+    auto args = RenderEngineCreationArgs::Builder()
+                        .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+                        .setImageCacheSize(1)
+                        .setEnableProtectedContext(true)
+                        .setPrecacheToneMapperShaderOnly(false)
+                        .setSupportsBackgroundBlur(true)
+                        .setContextPriority(RenderEngine::ContextPriority::REALTIME)
+                        .setRenderEngineType(type)
+                        .setUseColorManagerment(true)
+                        .build();
+    return RenderEngine::create(args);
+}
+
+static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_t width,
+                                                       uint32_t height,
+                                                       uint64_t extraUsageFlags = 0,
+                                                       std::string name = "output") {
+    return std::make_shared<ExternalTexture>(new GraphicBuffer(width, height,
+                                                               HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                               GRALLOC_USAGE_HW_RENDER |
+                                                                       GRALLOC_USAGE_HW_TEXTURE |
+                                                                       extraUsageFlags,
+                                                               std::move(name)),
+                                             re,
+                                             ExternalTexture::Usage::READABLE |
+                                                     ExternalTexture::Usage::WRITEABLE);
+}
+
+static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
+                                                   std::shared_ptr<ExternalTexture> original,
+                                                   uint64_t extraUsageFlags, std::string name) {
+    const uint32_t width = original->getBuffer()->getWidth();
+    const uint32_t height = original->getBuffer()->getHeight();
+    auto texture = allocateBuffer(re, width, height, extraUsageFlags, name);
+
+    const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+    DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .maxLuminance = 500,
+    };
+
+    const FloatRect layerRect(0, 0, width, height);
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = layerRect,
+                    },
+            .source =
+                    PixelSource{
+                            .buffer =
+                                    Buffer{
+                                            .buffer = original,
+                                    },
+                    },
+            .alpha = half(1.0f),
+    };
+    auto layers = std::vector<LayerSettings>{layer};
+
+    auto [status, drawFence] =
+            re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()).get();
+    sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+    waitFence->waitForever(LOG_TAG);
+    return texture;
+}
+
+/**
+ * Helper for timing calls to drawLayers.
+ *
+ * Caller needs to create RenderEngine and the LayerSettings, and this takes
+ * care of setting up the display, starting and stopping the timer, calling
+ * drawLayers, and saving (if --save is used).
+ *
+ * This times both the CPU and GPU work initiated by drawLayers. All work done
+ * outside of the for loop is excluded from the timing measurements.
+ */
+static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers,
+                            benchmark::State& benchState, const char* saveFileName) {
+    auto [width, height] = getDisplaySize();
+    auto outputBuffer = allocateBuffer(re, width, height);
+
+    const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+    DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .maxLuminance = 500,
+    };
+
+    // This loop starts and stops the timer.
+    for (auto _ : benchState) {
+        auto [status, drawFence] = re.drawLayers(display, layers, outputBuffer,
+                                                 kUseFrameBufferCache, base::unique_fd())
+                                           .get();
+        sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+        waitFence->waitForever(LOG_TAG);
+    }
+
+    if (renderenginebench::save() && saveFileName) {
+        // Copy to a CPU-accessible buffer so we can encode it.
+        outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode");
+
+        std::string outFile = base::GetExecutableDirectory();
+        outFile.append("/");
+        outFile.append(saveFileName);
+        outFile.append(".jpg");
+        renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer());
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//  Benchmarks
+///////////////////////////////////////////////////////////////////////////////
+
+void BM_blur(benchmark::State& benchState) {
+    auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range()));
+
+    // Initially use cpu access so we can decode into it with AImageDecoder.
+    auto [width, height] = getDisplaySize();
+    auto srcBuffer =
+            allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
+    {
+        std::string srcImage = base::GetExecutableDirectory();
+        srcImage.append("/resources/homescreen.png");
+        renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
+
+        // Now copy into GPU-only buffer for more realistic timing.
+        srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
+    }
+
+    const FloatRect layerRect(0, 0, width, height);
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = layerRect,
+                    },
+            .source =
+                    PixelSource{
+                            .buffer =
+                                    Buffer{
+                                            .buffer = srcBuffer,
+                                    },
+                    },
+            .alpha = half(1.0f),
+    };
+    LayerSettings blurLayer{
+            .geometry =
+                    Geometry{
+                            .boundaries = layerRect,
+                    },
+            .alpha = half(1.0f),
+            .skipContentDraw = true,
+            .backgroundBlurRadius = 60,
+    };
+
+    auto layers = std::vector<LayerSettings>{layer, blurLayer};
+    benchDrawLayers(*re, layers, benchState, "blurred");
+}
+
+BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded);
diff --git a/libs/renderengine/benchmark/RenderEngineBench.h b/libs/renderengine/benchmark/RenderEngineBench.h
new file mode 100644
index 0000000..1a25d77
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/GraphicBuffer.h>
+
+using namespace android;
+
+/**
+ * Utilities for running benchmarks.
+ */
+namespace renderenginebench {
+/**
+ * Parse RenderEngineBench-specific flags from the command line.
+ *
+ * --save Save the output buffer to a file to verify that it drew as
+ *  expected.
+ */
+void parseFlags(int argc, char** argv);
+
+/**
+ * Parse flags for '--help'
+ */
+void parseFlagsForHelp(int argc, char** argv);
+
+/**
+ * Whether to save the drawing result to a file.
+ *
+ * True if --save was used on the command line.
+ */
+bool save();
+
+/**
+ * Decode the image at 'path' into 'buffer'.
+ *
+ * Currently only used for debugging. The image will be scaled to fit the
+ * buffer if necessary.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void decode(const char* path, const sp<GraphicBuffer>& buffer);
+
+/**
+ * Encode the buffer to a jpeg.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer);
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/main.cpp b/libs/renderengine/benchmark/main.cpp
new file mode 100644
index 0000000..7a62853
--- /dev/null
+++ b/libs/renderengine/benchmark/main.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <RenderEngineBench.h>
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+    // Initialize will exit if it sees '--help', so check for it and print info
+    // about our flags first.
+    renderenginebench::parseFlagsForHelp(argc, argv);
+    benchmark::Initialize(&argc, argv);
+
+    // Calling this separately from parseFlagsForHelp prevents collisions with
+    // google-benchmark's flags, since Initialize will consume and remove flags
+    // it recognizes.
+    renderenginebench::parseFlags(argc, argv);
+    benchmark::RunSpecifiedBenchmarks();
+    return 0;
+}
diff --git a/libs/renderengine/benchmark/resources/homescreen.png b/libs/renderengine/benchmark/resources/homescreen.png
new file mode 100644
index 0000000..997b72d
--- /dev/null
+++ b/libs/renderengine/benchmark/resources/homescreen.png
Binary files differ
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 467f848..22dd866 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1078,15 +1078,16 @@
     return image;
 }
 
-status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
-                                      const std::vector<const LayerSettings*>& layers,
-                                      const std::shared_ptr<ExternalTexture>& buffer,
-                                      const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                                      base::unique_fd* drawFence) {
+void GLESRenderEngine::drawLayersInternal(
+        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
     ATRACE_CALL();
     if (layers.empty()) {
         ALOGV("Drawing empty layer stack");
-        return NO_ERROR;
+        resultPromise->set_value({NO_ERROR, base::unique_fd()});
+        return;
     }
 
     if (bufferFence.get() >= 0) {
@@ -1100,7 +1101,8 @@
 
     if (buffer == nullptr) {
         ALOGE("No output buffer provided. Aborting GPU composition.");
-        return BAD_VALUE;
+        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        return;
     }
 
     validateOutputBufferUsage(buffer->getBuffer());
@@ -1108,10 +1110,10 @@
     std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
     // Gathering layers that requested blur, we'll need them to decide when to render to an
     // offscreen buffer, and when to render to the native buffer.
-    std::deque<const LayerSettings*> blurLayers;
+    std::deque<const LayerSettings> blurLayers;
     if (CC_LIKELY(mBlurFilter != nullptr)) {
-        for (auto layer : layers) {
-            if (layer->backgroundBlurRadius > 0) {
+        for (const auto& layer : layers) {
+            if (layer.backgroundBlurRadius > 0) {
                 blurLayers.push_back(layer);
             }
         }
@@ -1128,18 +1130,20 @@
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                   buffer->getBuffer()->handle);
             checkErrors();
-            return fbo->getStatus();
+            resultPromise->set_value({fbo->getStatus(), base::unique_fd()});
+            return;
         }
         setViewportAndProjection(display.physicalDisplay, display.clip);
     } else {
         setViewportAndProjection(display.physicalDisplay, display.clip);
         auto status =
-                mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
+                mBlurFilter->setAsDrawTarget(display, blurLayers.front().backgroundBlurRadius);
         if (status != NO_ERROR) {
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
                   buffer->getBuffer()->handle);
             checkErrors();
-            return status;
+            resultPromise->set_value({status, base::unique_fd()});
+            return;
         }
     }
 
@@ -1156,10 +1160,6 @@
 
     const mat4 projectionMatrix =
             ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
-    if (!display.clearRegion.isEmpty()) {
-        glDisable(GL_BLEND);
-        fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
-    }
 
     Mesh mesh = Mesh::Builder()
                         .setPrimitive(Mesh::TRIANGLE_FAN)
@@ -1167,7 +1167,7 @@
                         .setTexCoords(2 /* size */)
                         .setCropCoords(2 /* size */)
                         .build();
-    for (auto const layer : layers) {
+    for (const auto& layer : layers) {
         if (blurLayers.size() > 0 && blurLayers.front() == layer) {
             blurLayers.pop_front();
 
@@ -1176,7 +1176,8 @@
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't render first blur pass");
-                return status;
+                resultPromise->set_value({status, base::unique_fd()});
+                return;
             }
 
             if (blurLayers.size() == 0) {
@@ -1192,13 +1193,14 @@
                 // There's still something else to blur, so let's keep rendering to our FBO
                 // instead of to the display.
                 status = mBlurFilter->setAsDrawTarget(display,
-                                                      blurLayers.front()->backgroundBlurRadius);
+                                                      blurLayers.front().backgroundBlurRadius);
             }
             if (status != NO_ERROR) {
                 ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't bind native framebuffer");
-                return status;
+                resultPromise->set_value({status, base::unique_fd()});
+                return;
             }
 
             status = mBlurFilter->render(blurLayersSize > 1);
@@ -1206,47 +1208,48 @@
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't render blur filter");
-                return status;
+                resultPromise->set_value({status, base::unique_fd()});
+                return;
             }
         }
 
         // Ensure luminance is at least 100 nits to avoid div-by-zero
-        const float maxLuminance = std::max(100.f, layer->source.buffer.maxLuminanceNits);
+        const float maxLuminance = std::max(100.f, layer.source.buffer.maxLuminanceNits);
         mState.maxMasteringLuminance = maxLuminance;
         mState.maxContentLuminance = maxLuminance;
-        mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
+        mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
 
-        const FloatRect bounds = layer->geometry.boundaries;
+        const FloatRect bounds = layer.geometry.boundaries;
         Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
         position[0] = vec2(bounds.left, bounds.top);
         position[1] = vec2(bounds.left, bounds.bottom);
         position[2] = vec2(bounds.right, bounds.bottom);
         position[3] = vec2(bounds.right, bounds.top);
 
-        setupLayerCropping(*layer, mesh);
-        setColorTransform(layer->colorTransform);
+        setupLayerCropping(layer, mesh);
+        setColorTransform(layer.colorTransform);
 
         bool usePremultipliedAlpha = true;
         bool disableTexture = true;
         bool isOpaque = false;
-        if (layer->source.buffer.buffer != nullptr) {
+        if (layer.source.buffer.buffer != nullptr) {
             disableTexture = false;
-            isOpaque = layer->source.buffer.isOpaque;
+            isOpaque = layer.source.buffer.isOpaque;
 
-            sp<GraphicBuffer> gBuf = layer->source.buffer.buffer->getBuffer();
+            sp<GraphicBuffer> gBuf = layer.source.buffer.buffer->getBuffer();
             validateInputBufferUsage(gBuf);
-            bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
-                                      layer->source.buffer.fence);
+            bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
+                                      layer.source.buffer.fence);
 
-            usePremultipliedAlpha = layer->source.buffer.usePremultipliedAlpha;
-            Texture texture(Texture::TEXTURE_EXTERNAL, layer->source.buffer.textureName);
-            mat4 texMatrix = layer->source.buffer.textureTransform;
+            usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+            Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+            mat4 texMatrix = layer.source.buffer.textureTransform;
 
             texture.setMatrix(texMatrix.asArray());
-            texture.setFiltering(layer->source.buffer.useTextureFiltering);
+            texture.setFiltering(layer.source.buffer.useTextureFiltering);
 
             texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
-            setSourceY410BT2020(layer->source.buffer.isY410BT2020);
+            setSourceY410BT2020(layer.source.buffer.isY410BT2020);
 
             renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
             texCoords[0] = vec2(0.0, 0.0);
@@ -1261,62 +1264,63 @@
             }
         }
 
-        const half3 solidColor = layer->source.solidColor;
-        const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer->alpha);
+        const half3 solidColor = layer.source.solidColor;
+        const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
         // Buffer sources will have a black solid color ignored in the shader,
         // so in that scenario the solid color passed here is arbitrary.
         setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
-                           layer->geometry.roundedCornersRadius);
-        if (layer->disableBlending) {
+                           layer.geometry.roundedCornersRadius);
+        if (layer.disableBlending) {
             glDisable(GL_BLEND);
         }
-        setSourceDataSpace(layer->sourceDataspace);
+        setSourceDataSpace(layer.sourceDataspace);
 
-        if (layer->shadow.length > 0.0f) {
-            handleShadow(layer->geometry.boundaries, layer->geometry.roundedCornersRadius,
-                         layer->shadow);
+        if (layer.shadow.length > 0.0f) {
+            handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
+                         layer.shadow);
         }
         // We only want to do a special handling for rounded corners when having rounded corners
         // is the only reason it needs to turn on blending, otherwise, we handle it like the
         // usual way since it needs to turn on blending anyway.
-        else if (layer->geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
-            handleRoundedCorners(display, *layer, mesh);
+        else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+            handleRoundedCorners(display, layer, mesh);
         } else {
             drawMesh(mesh);
         }
 
         // Cleanup if there's a buffer source
-        if (layer->source.buffer.buffer != nullptr) {
+        if (layer.source.buffer.buffer != nullptr) {
             disableBlending();
             setSourceY410BT2020(false);
             disableTexturing();
         }
     }
 
-    if (drawFence != nullptr) {
-        *drawFence = flush();
-    }
+    base::unique_fd drawFence = flush();
+
     // If flush failed or we don't support native fences, we need to force the
     // gl command stream to be executed.
-    if (drawFence == nullptr || drawFence->get() < 0) {
+    if (drawFence.get() < 0) {
         bool success = finish();
         if (!success) {
             ALOGE("Failed to flush RenderEngine commands");
             checkErrors();
             // Chances are, something illegal happened (either the caller passed
             // us bad parameters, or we messed up our shader generation).
-            return INVALID_OPERATION;
+            resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+            return;
         }
         mLastDrawFence = nullptr;
     } else {
         // The caller takes ownership of drawFence, so we need to duplicate the
         // fd here.
-        mLastDrawFence = new Fence(dup(drawFence->get()));
+        mLastDrawFence = new Fence(dup(drawFence.get()));
     }
     mPriorResourcesCleaned = false;
 
     checkErrors();
-    return NO_ERROR;
+    resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+    return;
 }
 
 void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 14627ce..1d7c2ca 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -63,11 +63,6 @@
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
     void useProtectedContext(bool useProtectedContext) override;
-    status_t drawLayers(const DisplaySettings& display,
-                        const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
     void cleanupPostRender() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
@@ -107,6 +102,11 @@
             EXCLUDES(mRenderingMutex);
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
     bool canSkipPostRenderCleanup() const override;
+    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+                            const DisplaySettings& display,
+                            const std::vector<LayerSettings>& layers,
+                            const std::shared_ptr<ExternalTexture>& buffer,
+                            const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
 
 private:
     friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 53fa622..b4cab39 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -51,25 +51,22 @@
     // dataspace, in non-linear space.
     mat4 colorTransform = mat4();
 
-    // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
-    // This is specified in layer-stack space.
-    Region clearRegion = Region::INVALID_REGION;
-
     // An additional orientation flag to be applied after clipping the output.
     // By way of example, this may be used for supporting fullscreen screenshot
     // capture of a device in landscape while the buffer is in portrait
     // orientation.
     uint32_t orientation = ui::Transform::ROT_0;
 
-    // SDR white point, -1f if unknown
-    float sdrWhitePointNits = -1.f;
+    // Target luminance of the display. -1f if unknown.
+    // All layers will be dimmed by (max(layer white points) / targetLuminanceNits).
+    // If the target luminance is unknown, then no display-level dimming occurs.
+    float targetLuminanceNits = -1.f;
 };
 
 static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
     return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
             lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace &&
-            lhs.colorTransform == rhs.colorTransform &&
-            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+            lhs.colorTransform == rhs.colorTransform && lhs.orientation == rhs.orientation;
 }
 
 // Defining PrintTo helps with Google Tests.
@@ -84,9 +81,6 @@
     PrintTo(settings.outputDataspace, os);
     *os << "\n    .colorTransform = " << settings.colorTransform;
     *os << "\n    .clearRegion = ";
-    PrintTo(settings.clearRegion, os);
-    *os << "\n    .orientation = " << settings.orientation;
-    *os << "\n}";
 }
 
 } // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 715f3f8..702e8b0 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -171,6 +171,12 @@
 
     // Name associated with the layer for debugging purposes.
     std::string name;
+
+    // Luminance of the white point for this layer. Used for linear dimming.
+    // Individual layers will be dimmed by (whitePointNits / maxWhitePoint).
+    // If white point nits are unknown, then this layer is assumed to have the
+    // same luminance as the brightest layer in the scene.
+    float whitePointNits = -1.f;
 };
 
 // Keep in sync with custom comparison function in
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index f555cdb..d646756 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -68,6 +68,7 @@
 class Mesh;
 class Texture;
 struct RenderEngineCreationArgs;
+struct RenderEngineResult;
 
 namespace threaded {
 class RenderEngineThreaded;
@@ -98,7 +99,7 @@
         SKIA_GL_THREADED = 4,
     };
 
-    static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args);
+    static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
 
     virtual ~RenderEngine() = 0;
 
@@ -156,17 +157,12 @@
     // parameter does nothing.
     // @param bufferFence Fence signalling that the buffer is ready to be drawn
     // to.
-    // @param drawFence A pointer to a fence, which will fire when the buffer
-    // has been drawn to and is ready to be examined. The fence will be
-    // initialized by this method. The caller will be responsible for owning the
-    // fence.
-    // @return An error code indicating whether drawing was successful. For
-    // now, this always returns NO_ERROR.
-    virtual status_t drawLayers(const DisplaySettings& display,
-                                const std::vector<const LayerSettings*>& layers,
-                                const std::shared_ptr<ExternalTexture>& buffer,
-                                const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                                base::unique_fd* drawFence) = 0;
+    // @return A future object of RenderEngineResult struct indicating whether
+    // drawing was successful in async mode.
+    virtual std::future<RenderEngineResult> drawLayers(
+            const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+            const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+            base::unique_fd&& bufferFence);
 
     // Clean-up method that should be called on the main thread after the
     // drawFence returned by drawLayers fires. This method will free up
@@ -193,6 +189,10 @@
     static void validateInputBufferUsage(const sp<GraphicBuffer>&);
     static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
 
+    // Allows flinger to get the render engine thread id for power management with ADPF
+    // Returns the tid of the renderengine thread if it's threaded, and std::nullopt otherwise
+    virtual std::optional<pid_t> getRenderEngineTid() const { return std::nullopt; }
+
 protected:
     RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
 
@@ -232,6 +232,12 @@
     friend class threaded::RenderEngineThreaded;
     friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
     const RenderEngineType mRenderEngineType;
+
+    virtual void drawLayersInternal(
+            const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+            const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+            const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+            base::unique_fd&& bufferFence) = 0;
 };
 
 struct RenderEngineCreationArgs {
@@ -318,6 +324,13 @@
             RenderEngine::RenderEngineType::SKIA_GL_THREADED;
 };
 
+struct RenderEngineResult {
+    // status indicates if drawing is successful
+    status_t status;
+    // drawFence will fire when the buffer has been drawn to and is ready to be examined.
+    base::unique_fd drawFence;
+};
+
 } // namespace renderengine
 } // namespace android
 
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 0be3ba6..248bd65 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -47,10 +47,15 @@
     MOCK_METHOD1(useProtectedContext, void(bool));
     MOCK_METHOD0(cleanupPostRender, void());
     MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
-    MOCK_METHOD6(drawLayers,
-                 status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
-                          const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
-                          base::unique_fd*));
+    MOCK_METHOD5(drawLayers,
+                 std::future<RenderEngineResult>(const DisplaySettings&,
+                                                 const std::vector<LayerSettings>&,
+                                                 const std::shared_ptr<ExternalTexture>&,
+                                                 const bool, base::unique_fd&&));
+    MOCK_METHOD6(drawLayersInternal,
+                 void(const std::shared_ptr<std::promise<RenderEngineResult>>&&,
+                      const DisplaySettings&, const std::vector<LayerSettings>&,
+                      const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&));
     MOCK_METHOD0(cleanFramebufferCache, void());
     MOCK_METHOD0(getContextPriority, int());
     MOCK_METHOD0(supportsBackgroundBlur, bool());
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index cc627b8..b18a872 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -95,25 +95,27 @@
             .alpha = 1,
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer, &caster};
-    // When sourceDataspace matches dest, the general shadow fragment shader doesn't
-    // have color correction added.
-    // independently, when it is not srgb, the *vertex* shader has color correction added.
-    // This may be a bug, but the shader still needs to be cached as it is triggered
-    // during youtube pip.
-    for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
-        layer.sourceDataspace = dataspace;
-        // The 2nd matrix, which has different scales for x and y, will
-        // generate the slower (more general case) shadow shader
-        for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) {
-            layer.geometry.positionTransform = transform;
-            caster.geometry.positionTransform = transform;
-            for (bool translucent : {false, true}){
-                layer.shadow.casterIsTranslucent = translucent;
-                renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                        base::unique_fd(), nullptr);
-            }
-        }
+    auto layers = std::vector<LayerSettings>{layer, caster};
+    // Four combinations of settings are used (two transforms here, and drawShadowLayers is
+    // called with two different destination data spaces) They're all rounded rect.
+    // Three of these are cache misses that generate new shaders.
+    // The first combination generates a short and simple shadow shader.
+    // The second combination, flip transform, generates two shaders. The first appears to involve
+    //   gaussian_fp. The second is a long and general purpose shadow shader with a device space
+    //   transformation stage.
+    // The third combination is a cache hit, nothing new.
+    // The fourth combination, flip transform with a non-SRGB destination dataspace, is new.
+    //   It is unique in that nearly everything is done in the vertex shader, and that vertex shader
+    //   requires color correction. This is triggered differently from every other instance of color
+    //   correction. All other instances are triggered when src and dst dataspaces differ, while
+    //   this one is triggered by the destination being non-srgb. Apparently since the third
+    //   combination is a cache hit, this color correction is only added when the vertex shader is
+    //   doing something non-trivial.
+    for (auto transform : {mat4(), kFlip}) {
+        layer.geometry.positionTransform = transform;
+        caster.geometry.positionTransform = transform;
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                 base::unique_fd());
     }
 }
 
@@ -138,7 +140,7 @@
                                           }},
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer};
+    auto layers = std::vector<LayerSettings>{layer};
     for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
         layer.sourceDataspace = dataspace;
         // Cache shaders for both rects and round rects.
@@ -151,7 +153,7 @@
                 for (auto alpha : {half(.2f), half(1.0f)}) {
                     layer.alpha = alpha;
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), nullptr);
+                                             base::unique_fd());
                 }
             }
         }
@@ -174,13 +176,13 @@
             .alpha = 0.5,
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer};
+    auto layers = std::vector<LayerSettings>{layer};
     for (auto transform : {mat4(), kScaleAndTranslate}) {
         layer.geometry.positionTransform = transform;
         for (float roundedCornersRadius : {0.0f, 50.f}) {
             layer.geometry.roundedCornersRadius = roundedCornersRadius;
             renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                     base::unique_fd(), nullptr);
+                                     base::unique_fd());
         }
     }
 }
@@ -199,12 +201,12 @@
             .skipContentDraw = true,
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer};
+    auto layers = std::vector<LayerSettings>{layer};
     // Different blur code is invoked for radii less and greater than 30 pixels
     for (int radius : {9, 60}) {
         layer.backgroundBlurRadius = radius;
         renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                 base::unique_fd(), nullptr);
+                                 base::unique_fd());
     }
 }
 
@@ -240,7 +242,7 @@
                     },
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer};
+    auto layers = std::vector<LayerSettings>{layer};
     for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
         layer.source = pixelSource;
         for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
@@ -251,7 +253,7 @@
                 for (float alpha : {0.5f, 1.f}) {
                     layer.alpha = alpha,
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), nullptr);
+                                             base::unique_fd());
                 }
             }
         }
@@ -287,9 +289,8 @@
 
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer};
-    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                             base::unique_fd(), nullptr);
+    auto layers = std::vector<LayerSettings>{layer};
+    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
 }
 
 static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -316,9 +317,8 @@
 
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer};
-    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                            base::unique_fd(), nullptr);
+    auto layers = std::vector<LayerSettings>{layer};
+    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
 }
 
 //
@@ -381,6 +381,7 @@
                                                           ExternalTexture::Usage::WRITEABLE);
         drawHolePunchLayer(renderengine, display, dstTexture);
         drawSolidLayers(renderengine, display, dstTexture);
+
         drawShadowLayers(renderengine, display, srcTexture);
         drawShadowLayers(renderengine, p3Display, srcTexture);
 
@@ -424,6 +425,16 @@
 
         drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
 
+        // draw one final layer synchronously to force GL submit
+        LayerSettings layer{
+                .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)},
+        };
+        auto layers = std::vector<LayerSettings>{layer};
+        // call get() to make it synchronous
+        renderengine
+                ->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd())
+                .get();
+
         const nsecs_t timeAfter = systemTime();
         const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
         const int shadersCompiled = renderengine->reportShadersCompiled();
diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp
index ff4d348..f367a84 100644
--- a/libs/renderengine/skia/ColorSpaces.cpp
+++ b/libs/renderengine/skia/ColorSpaces.cpp
@@ -20,6 +20,7 @@
 namespace renderengine {
 namespace skia {
 
+// please keep in sync with hwui/utils/Color.cpp
 sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
     skcms_Matrix3x3 gamut;
     switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -32,6 +33,17 @@
         case HAL_DATASPACE_STANDARD_DCI_P3:
             gamut = SkNamedGamut::kDisplayP3;
             break;
+        case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+            gamut = SkNamedGamut::kAdobeRGB;
+            break;
+        case HAL_DATASPACE_STANDARD_BT601_625:
+        case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+        case HAL_DATASPACE_STANDARD_BT601_525:
+        case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+        case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+        case HAL_DATASPACE_STANDARD_BT470M:
+        case HAL_DATASPACE_STANDARD_FILM:
+        case HAL_DATASPACE_STANDARD_UNSPECIFIED:
         default:
             gamut = SkNamedGamut::kSRGB;
             break;
@@ -42,10 +54,19 @@
             return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
         case HAL_DATASPACE_TRANSFER_SRGB:
             return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+        case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+            return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+        case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+            return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+        case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+            return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
         case HAL_DATASPACE_TRANSFER_ST2084:
             return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
         case HAL_DATASPACE_TRANSFER_HLG:
             return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+        case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
         default:
             return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
     }
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index e42b5b9..cc90946 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -46,6 +46,7 @@
 #include <cmath>
 #include <cstdint>
 #include <memory>
+#include <numeric>
 
 #include "../gl/GLExtensions.h"
 #include "Cache.h"
@@ -53,6 +54,8 @@
 #include "SkBlendMode.h"
 #include "SkImageInfo.h"
 #include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
 #include "filters/LinearEffect.h"
 #include "log/log_main.h"
 #include "skia/debug/SkiaCapture.h"
@@ -328,7 +331,7 @@
 
     if (args.supportsBackgroundBlur) {
         ALOGD("Background Blurs Enabled");
-        mBlurFilter = new BlurFilter();
+        mBlurFilter = new KawaseBlurFilter();
     }
     mCapture = std::make_unique<SkiaCapture>();
 }
@@ -611,31 +614,32 @@
 };
 
 sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
-        sk_sp<SkShader> shader,
-        const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
-        bool requiresLinearEffect) {
-    const auto stretchEffect = layer->stretchEffect;
+        const RuntimeEffectShaderParameters& parameters) {
     // The given surface will be stretched by HWUI via matrix transformation
     // which gets similar results for most surfaces
     // Determine later on if we need to leverage the stertch shader within
     // surface flinger
+    const auto& stretchEffect = parameters.layer.stretchEffect;
+    auto shader = parameters.shader;
     if (stretchEffect.hasEffect()) {
-        const auto targetBuffer = layer->source.buffer.buffer;
+        const auto targetBuffer = parameters.layer.source.buffer.buffer;
         const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
-        if (graphicBuffer && shader) {
+        if (graphicBuffer && parameters.shader) {
             shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
         }
     }
 
-    if (requiresLinearEffect) {
-        const ui::Dataspace inputDataspace =
-                mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
-        const ui::Dataspace outputDataspace =
-                mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
+    if (parameters.requiresLinearEffect) {
+        const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
+                                                                 : ui::Dataspace::V0_SRGB_LINEAR;
+        const ui::Dataspace outputDataspace = mUseColorManagement
+                ? parameters.display.outputDataspace
+                : ui::Dataspace::V0_SRGB_LINEAR;
 
-        LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
-                                           .outputDataspace = outputDataspace,
-                                           .undoPremultipliedAlpha = undoPremultipliedAlpha};
+        auto effect =
+                shaders::LinearEffect{.inputDataspace = inputDataspace,
+                                      .outputDataspace = outputDataspace,
+                                      .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
 
         auto effectIter = mRuntimeEffects.find(effect);
         sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -645,16 +649,16 @@
         } else {
             runtimeEffect = effectIter->second;
         }
-        float maxLuminance = layer->source.buffer.maxLuminanceNits;
-        // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
-        // white point
-        if (maxLuminance <= 0.f) {
-            maxLuminance = display.sdrWhitePointNits;
-        }
-        return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
-                                        display.maxLuminance, maxLuminance);
+        mat4 colorTransform = parameters.layer.colorTransform;
+
+        colorTransform *=
+                mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
+                                 parameters.layerDimmingRatio, 1.f));
+        return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
+                                        parameters.display.maxLuminance,
+                                        parameters.layer.source.buffer.maxLuminanceNits);
     }
-    return shader;
+    return parameters.shader;
 }
 
 void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
@@ -727,22 +731,29 @@
     return roundedRect;
 }
 
-status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
-                                        const std::vector<const LayerSettings*>& layers,
-                                        const std::shared_ptr<ExternalTexture>& buffer,
-                                        const bool /*useFramebufferCache*/,
-                                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+static bool equalsWithinMargin(float expected, float value, float margin) {
+    LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
+    return std::abs(expected - value) < margin;
+}
+
+void SkiaGLRenderEngine::drawLayersInternal(
+        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
+        base::unique_fd&& bufferFence) {
     ATRACE_NAME("SkiaGL::drawLayers");
 
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     if (layers.empty()) {
         ALOGV("Drawing empty layer stack");
-        return NO_ERROR;
+        resultPromise->set_value({NO_ERROR, base::unique_fd()});
+        return;
     }
 
     if (buffer == nullptr) {
         ALOGE("No output buffer provided. Aborting GPU composition.");
-        return BAD_VALUE;
+        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        return;
     }
 
     validateOutputBufferUsage(buffer->getBuffer());
@@ -774,7 +785,8 @@
     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
     if (dstCanvas == nullptr) {
         ALOGE("Cannot acquire canvas from Skia.");
-        return BAD_VALUE;
+        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        return;
     }
 
     // setup color filter if necessary
@@ -785,6 +797,18 @@
     const bool ctModifiesAlpha =
             displayColorTransform && !displayColorTransform->isAlphaUnchanged();
 
+    // Find the max layer white point to determine the max luminance of the scene...
+    const float maxLayerWhitePoint = std::transform_reduce(
+            layers.cbegin(), layers.cend(), 0.f,
+            [](float left, float right) { return std::max(left, right); },
+            [&](const auto& l) { return l.whitePointNits; });
+
+    // ...and compute the dimming ratio if dimming is requested
+    const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
+                    maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+            ? maxLayerWhitePoint / display.targetLuminanceNits
+            : 1.f;
+
     // Find if any layers have requested blur, we'll use that info to decide when to render to an
     // offscreen buffer and when to render to the native buffer.
     sk_sp<SkSurface> activeSurface(dstSurface);
@@ -798,19 +822,19 @@
             if (!layerHasBlur(layer, ctModifiesAlpha)) {
                 continue;
             }
-            if (layer->backgroundBlurRadius > 0 &&
-                layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
+            if (layer.backgroundBlurRadius > 0 &&
+                layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
                 requiresCompositionLayer = true;
             }
-            for (auto region : layer->blurRegions) {
-                if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+            for (auto region : layer.blurRegions) {
+                if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
                     requiresCompositionLayer = true;
                 }
             }
             if (requiresCompositionLayer) {
                 activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
                 canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
-                blurCompositionLayer = layer;
+                blurCompositionLayer = &layer;
                 break;
             }
         }
@@ -821,34 +845,12 @@
     canvas->clear(SK_ColorTRANSPARENT);
     initCanvas(canvas, display);
 
-    // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
-    // view is still on-screen. The clear region could be re-specified as a black color layer,
-    // however.
-    if (!display.clearRegion.isEmpty()) {
-        ATRACE_NAME("ClearRegion");
-        size_t numRects = 0;
-        Rect const* rects = display.clearRegion.getArray(&numRects);
-        SkIRect skRects[numRects];
-        for (int i = 0; i < numRects; ++i) {
-            skRects[i] =
-                    SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
-        }
-        SkRegion clearRegion;
-        SkPaint paint;
-        sk_sp<SkShader> shader =
-                SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
-                                 toSkColorSpace(dstDataspace));
-        paint.setShader(shader);
-        clearRegion.setRects(skRects, numRects);
-        canvas->drawRegion(clearRegion, paint);
-    }
-
     for (const auto& layer : layers) {
-        ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str());
+        ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
 
         if (kPrintLayerSettings) {
             std::stringstream ls;
-            PrintTo(*layer, &ls);
+            PrintTo(layer, &ls);
             auto debugs = ls.str();
             int pos = 0;
             while (pos < debugs.size()) {
@@ -858,7 +860,7 @@
         }
 
         sk_sp<SkImage> blurInput;
-        if (blurCompositionLayer == layer) {
+        if (blurCompositionLayer == &layer) {
             LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
             LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
 
@@ -897,17 +899,17 @@
         if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
             // Record the name of the layer if the capture is running.
             std::stringstream layerSettings;
-            PrintTo(*layer, &layerSettings);
+            PrintTo(layer, &layerSettings);
             // Store the LayerSettings in additional information.
-            canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
+            canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(),
                                    SkData::MakeWithCString(layerSettings.str().c_str()));
         }
         // Layers have a local transform that should be applied to them
-        canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
+        canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());
 
         const auto [bounds, roundRectClip] =
-                getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop,
-                                 layer->geometry.roundedCornersRadius);
+                getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
+                                 layer.geometry.roundedCornersRadius);
         if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
             std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
 
@@ -928,20 +930,19 @@
 
             // TODO(b/182216890): Filter out empty layers earlier
             if (blurRect.width() > 0 && blurRect.height() > 0) {
-                if (layer->backgroundBlurRadius > 0) {
+                if (layer.backgroundBlurRadius > 0) {
                     ATRACE_NAME("BackgroundBlur");
-                    auto blurredImage =
-                            mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
-                                                  blurRect);
+                    auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius,
+                                                              blurInput, blurRect);
 
-                    cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+                    cachedBlurs[layer.backgroundBlurRadius] = blurredImage;
 
-                    mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+                    mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f,
                                                 blurRect, blurredImage, blurInput);
                 }
 
-                canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
-                for (auto region : layer->blurRegions) {
+                canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
+                for (auto region : layer.blurRegions) {
                     if (cachedBlurs[region.blurRadius] == nullptr) {
                         ATRACE_NAME("BlurRegion");
                         cachedBlurs[region.blurRadius] =
@@ -956,19 +957,18 @@
             }
         }
 
-        if (layer->shadow.length > 0) {
+        if (layer.shadow.length > 0) {
             // This would require a new parameter/flag to SkShadowUtils::DrawShadow
-            LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
+            LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow");
 
             SkRRect shadowBounds, shadowClip;
-            if (layer->geometry.boundaries == layer->shadow.boundaries) {
+            if (layer.geometry.boundaries == layer.shadow.boundaries) {
                 shadowBounds = bounds;
                 shadowClip = roundRectClip;
             } else {
                 std::tie(shadowBounds, shadowClip) =
-                        getBoundsAndClip(layer->shadow.boundaries,
-                                         layer->geometry.roundedCornersCrop,
-                                         layer->geometry.roundedCornersRadius);
+                        getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop,
+                                         layer.geometry.roundedCornersRadius);
             }
 
             // Technically, if bounds is a rect and roundRectClip is not empty,
@@ -979,18 +979,21 @@
             // looks more like the intent.
             const auto& rrect =
                     shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
-            drawShadow(canvas, rrect, layer->shadow);
+            drawShadow(canvas, rrect, layer.shadow);
         }
 
-        const bool requiresLinearEffect = layer->colorTransform != mat4() ||
+        const float layerDimmingRatio = layer.whitePointNits <= 0.f
+                ? displayDimmingRatio
+                : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
+
+        const bool requiresLinearEffect = layer.colorTransform != mat4() ||
                 (mUseColorManagement &&
-                 needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
-                (display.sdrWhitePointNits > 0.f &&
-                 display.sdrWhitePointNits != display.maxLuminance);
+                 needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
+                !equalsWithinMargin(1.f, layerDimmingRatio, 0.001f);
 
         // quick abort from drawing the remaining portion of the layer
-        if (layer->skipContentDraw ||
-            (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
+        if (layer.skipContentDraw ||
+            (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending &&
              (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
             continue;
         }
@@ -1000,13 +1003,13 @@
         // management is a no-op.
         const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
                 ? dstDataspace
-                : layer->sourceDataspace;
+                : layer.sourceDataspace;
 
         SkPaint paint;
-        if (layer->source.buffer.buffer) {
+        if (layer.source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
-            validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
-            const auto& item = layer->source.buffer;
+            validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
+            const auto& item = layer.source.buffer;
             std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
 
             if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
@@ -1025,8 +1028,8 @@
 
             // if the layer's buffer has a fence, then we must must respect the fence prior to using
             // the buffer.
-            if (layer->source.buffer.fence != nullptr) {
-                waitFence(layer->source.buffer.fence->get());
+            if (layer.source.buffer.fence != nullptr) {
+                waitFence(layer.source.buffer.fence->get());
             }
 
             // isOpaque means we need to ignore the alpha in the image,
@@ -1070,7 +1073,7 @@
 
             sk_sp<SkShader> shader;
 
-            if (layer->source.buffer.useTextureFiltering) {
+            if (layer.source.buffer.useTextureFiltering) {
                 shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
                                            SkSamplingOptions(
                                                    {SkFilterMode::kLinear, SkMipmapMode::kNone}),
@@ -1085,24 +1088,39 @@
                                                            toSkColorSpace(layerDataspace)));
             }
 
-            paint.setShader(createRuntimeEffectShader(shader, layer, display,
-                                                      !item.isOpaque && item.usePremultipliedAlpha,
-                                                      requiresLinearEffect));
-            paint.setAlphaf(layer->alpha);
+            paint.setShader(createRuntimeEffectShader(
+                    RuntimeEffectShaderParameters{.shader = shader,
+                                                  .layer = layer,
+                                                  .display = display,
+                                                  .undoPremultipliedAlpha = !item.isOpaque &&
+                                                          item.usePremultipliedAlpha,
+                                                  .requiresLinearEffect = requiresLinearEffect,
+                                                  .layerDimmingRatio = layerDimmingRatio}));
+
+            // Turn on dithering when dimming beyond this threshold.
+            static constexpr float kDimmingThreshold = 0.2f;
+            if (layerDimmingRatio <= kDimmingThreshold) {
+                paint.setDither(true);
+            }
+            paint.setAlphaf(layer.alpha);
         } else {
             ATRACE_NAME("DrawColor");
-            const auto color = layer->source.solidColor;
+            const auto color = layer.source.solidColor;
             sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
                                                                 .fG = color.g,
                                                                 .fB = color.b,
-                                                                .fA = layer->alpha},
+                                                                .fA = layer.alpha},
                                                       toSkColorSpace(layerDataspace));
-            paint.setShader(createRuntimeEffectShader(shader, layer, display,
-                                                      /* undoPremultipliedAlpha */ false,
-                                                      requiresLinearEffect));
+            paint.setShader(createRuntimeEffectShader(
+                    RuntimeEffectShaderParameters{.shader = shader,
+                                                  .layer = layer,
+                                                  .display = display,
+                                                  .undoPremultipliedAlpha = false,
+                                                  .requiresLinearEffect = requiresLinearEffect,
+                                                  .layerDimmingRatio = layerDimmingRatio}));
         }
 
-        if (layer->disableBlending) {
+        if (layer.disableBlending) {
             paint.setBlendMode(SkBlendMode::kSrc);
         }
 
@@ -1131,13 +1149,11 @@
         activeSurface->flush();
     }
 
-    if (drawFence != nullptr) {
-        *drawFence = flush();
-    }
+    base::unique_fd drawFence = flush();
 
     // If flush failed or we don't support native fences, we need to force the
     // gl command stream to be executed.
-    bool requireSync = drawFence == nullptr || drawFence->get() < 0;
+    bool requireSync = drawFence.get() < 0;
     if (requireSync) {
         ATRACE_BEGIN("Submit(sync=true)");
     } else {
@@ -1149,11 +1165,13 @@
         ALOGE("Failed to flush RenderEngine commands");
         // Chances are, something illegal happened (either the caller passed
         // us bad parameters, or we messed up our shader generation).
-        return INVALID_OPERATION;
+        resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+        return;
     }
 
     // checkErrors();
-    return NO_ERROR;
+    resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+    return;
 }
 
 inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
@@ -1164,6 +1182,73 @@
     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
 }
 
+/**
+ *  Verifies that common, simple bounds + clip combinations can be converted into
+ *  a single RRect draw call returning true if possible. If true the radii parameter
+ *  will be filled with the correct radii values that combined with bounds param will
+ *  produce the insected roundRect. If false, the returned state of the radii param is undefined.
+ */
+static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
+                                    const SkRect& insetCrop, float cornerRadius,
+                                    SkVector radii[4]) {
+    const bool leftEqual = bounds.fLeft == crop.fLeft;
+    const bool topEqual = bounds.fTop == crop.fTop;
+    const bool rightEqual = bounds.fRight == crop.fRight;
+    const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+    // In the event that the corners of the bounds only partially align with the crop we
+    // need to ensure that the resulting shape can still be represented as a round rect.
+    // In particular the round rect implementation will scale the value of all corner radii
+    // if the sum of the radius along any edge is greater than the length of that edge.
+    // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
+    const bool requiredWidth = bounds.width() > (cornerRadius * 2);
+    const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+    if (!requiredWidth || !requiredHeight) {
+        return false;
+    }
+
+    // Check each cropped corner to ensure that it exactly matches the crop or its corner is
+    // contained within the cropped shape and does not need rounded.
+    // compute the UpperLeft corner radius
+    if (leftEqual && topEqual) {
+        radii[0].set(cornerRadius, cornerRadius);
+    } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+               (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
+        radii[0].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the UpperRight corner radius
+    if (rightEqual && topEqual) {
+        radii[1].set(cornerRadius, cornerRadius);
+    } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+               (topEqual && bounds.fRight <= insetCrop.fRight)) {
+        radii[1].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the BottomRight corner radius
+    if (rightEqual && bottomEqual) {
+        radii[2].set(cornerRadius, cornerRadius);
+    } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+               (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
+        radii[2].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the BottomLeft corner radius
+    if (leftEqual && bottomEqual) {
+        radii[3].set(cornerRadius, cornerRadius);
+    } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+               (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
+        radii[3].set(0, 0);
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
 inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
                                                                         const FloatRect& cropRect,
                                                                         const float cornerRadius) {
@@ -1181,66 +1266,20 @@
         // converting them to a single RRect draw. It is possible there are other cases
         // that can be converted.
         if (crop.contains(bounds)) {
-            bool intersectionIsRoundRect = true;
-            // check each cropped corner to ensure that it exactly matches the crop or is full
-            SkVector radii[4];
-
             const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
-
-            const bool leftEqual = bounds.fLeft == crop.fLeft;
-            const bool topEqual = bounds.fTop == crop.fTop;
-            const bool rightEqual = bounds.fRight == crop.fRight;
-            const bool bottomEqual = bounds.fBottom == crop.fBottom;
-
-            // compute the UpperLeft corner radius
-            if (leftEqual && topEqual) {
-                radii[0].set(cornerRadius, cornerRadius);
-            } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
-                       (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
-                       insetCrop.contains(bounds.fLeft, bounds.fTop)) {
-                radii[0].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the UpperRight corner radius
-            if (rightEqual && topEqual) {
-                radii[1].set(cornerRadius, cornerRadius);
-            } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
-                       (topEqual && bounds.fRight <= insetCrop.fRight) ||
-                       insetCrop.contains(bounds.fRight, bounds.fTop)) {
-                radii[1].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the BottomRight corner radius
-            if (rightEqual && bottomEqual) {
-                radii[2].set(cornerRadius, cornerRadius);
-            } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
-                       (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
-                       insetCrop.contains(bounds.fRight, bounds.fBottom)) {
-                radii[2].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the BottomLeft corner radius
-            if (leftEqual && bottomEqual) {
-                radii[3].set(cornerRadius, cornerRadius);
-            } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
-                       (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
-                       insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
-                radii[3].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
+            if (insetCrop.contains(bounds)) {
+                return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
             }
 
-            if (intersectionIsRoundRect) {
+            SkVector radii[4];
+            if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
                 SkRRect intersectionBounds;
                 intersectionBounds.setRectRadii(bounds, radii);
                 return {intersectionBounds, clip};
             }
         }
 
-        // we didn't it any of our fast paths so set the clip to the cropRect
+        // we didn't hit any of our fast paths so set the clip to the cropRect
         clip.setRectXY(crop, cornerRadius, cornerRadius);
     }
 
@@ -1249,13 +1288,13 @@
     return {SkRRect::MakeRect(bounds), clip};
 }
 
-inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer,
+inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings& layer,
                                              bool colorTransformModifiesAlpha) {
-    if (layer->backgroundBlurRadius > 0 || layer->blurRegions.size()) {
+    if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) {
         // return false if the content is opaque and would therefore occlude the blur
-        const bool opaqueContent = !layer->source.buffer.buffer || layer->source.buffer.isOpaque;
-        const bool opaqueAlpha = layer->alpha == 1.0f && !colorTransformModifiesAlpha;
-        return layer->skipContentDraw || !(opaqueContent && opaqueAlpha);
+        const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque;
+        const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha;
+        return layer.skipContentDraw || !(opaqueContent && opaqueAlpha);
     }
     return false;
 }
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index e1162f5..a650313 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -54,11 +54,6 @@
     ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
 
     std::future<void> primeCache() override;
-    status_t drawLayers(const DisplaySettings& display,
-                        const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
     void cleanupPostRender() override;
     void cleanFramebufferCache() override{};
     int getContextPriority() override;
@@ -77,6 +72,11 @@
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
     bool canSkipPostRenderCleanup() const override;
+    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+                            const DisplaySettings& display,
+                            const std::vector<LayerSettings>& layers,
+                            const std::shared_ptr<ExternalTexture>& buffer,
+                            const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
 
 private:
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -92,7 +92,7 @@
     inline SkRect getSkRect(const Rect& layer);
     inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
                                                         const FloatRect& crop, float cornerRadius);
-    inline bool layerHasBlur(const LayerSettings* layer, bool colorTransformModifiesAlpha);
+    inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
     inline SkColor getSkColor(const vec4& color);
     inline SkM44 getSkM44(const mat4& matrix);
     inline SkPoint3 getSkPoint3(const vec3& vector);
@@ -106,13 +106,18 @@
     void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
     void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                     const ShadowSettings& shadowSettings);
+
     // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
     // Otherwise it returns the input shader.
-    sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader,
-                                              const LayerSettings* layer,
-                                              const DisplaySettings& display,
-                                              bool undoPremultipliedAlpha,
-                                              bool requiresLinearEffect);
+    struct RuntimeEffectShaderParameters {
+        sk_sp<SkShader> shader;
+        const LayerSettings& layer;
+        const DisplaySettings& display;
+        bool undoPremultipliedAlpha;
+        bool requiresLinearEffect;
+        float layerDimmingRatio;
+    };
+    sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
 
     EGLDisplay mEGLDisplay;
     EGLContext mEGLContext;
@@ -134,7 +139,8 @@
     // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
     std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
             GUARDED_BY(mRenderingMutex);
-    std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+    std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
+            mRuntimeEffects;
     AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
 
     StretchShaderFactory mStretchShaderFactory;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 7cd9eca..eb65e83 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -44,14 +44,6 @@
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
     virtual bool isProtected() const override { return false; } // mInProtectedContext; }
     virtual bool supportsProtectedContent() const override { return false; };
-    virtual status_t drawLayers(const DisplaySettings& /*display*/,
-                                const std::vector<const LayerSettings*>& /*layers*/,
-                                const std::shared_ptr<ExternalTexture>& /*buffer*/,
-                                const bool /*useFramebufferCache*/,
-                                base::unique_fd&& /*bufferFence*/,
-                                base::unique_fd* /*drawFence*/) override {
-        return 0;
-    };
     virtual int getContextPriority() override { return 0; }
     virtual void assertShadersCompiled(int numShaders) {}
     virtual int reportShadersCompiled() { return 0; }
@@ -60,6 +52,14 @@
     virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
                                           bool /*isRenderable*/) override = 0;
     virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
+
+    virtual void drawLayersInternal(
+            const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+            const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+            const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+            base::unique_fd&& bufferFence) override {
+        resultPromise->set_value({NO_ERROR, base::unique_fd()});
+    };
 };
 
 } // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 7c5bee9..6746e47 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
 #include "BlurFilter.h"
 #include <SkCanvas.h>
 #include <SkData.h>
@@ -32,40 +31,14 @@
 namespace renderengine {
 namespace skia {
 
-BlurFilter::BlurFilter() {
-    SkString blurString(R"(
-        uniform shader input;
-        uniform float2 in_blurOffset;
-        uniform float2 in_maxSizeXY;
-
-        half4 main(float2 xy) {
-            half4 c = sample(input, xy);
-            c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
-                                       clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-            c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
-                                       clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-            c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
-                                       clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-            c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
-                                       clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-
-            return half4(c.rgb * 0.2, 1.0);
-        }
-    )");
-
-    auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
-    if (!blurEffect) {
-        LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
-    }
-    mBlurEffect = std::move(blurEffect);
-
+static sk_sp<SkRuntimeEffect> createMixEffect() {
     SkString mixString(R"(
         uniform shader blurredInput;
         uniform shader originalInput;
         uniform float mixFactor;
 
         half4 main(float2 xy) {
-            return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
+            return half4(mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor));
         }
     )");
 
@@ -73,58 +46,12 @@
     if (!mixEffect) {
         LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
     }
-    mMixEffect = std::move(mixEffect);
+    return mixEffect;
 }
 
-sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
-                                    const sk_sp<SkImage> input, const SkRect& blurRect) const {
-    // Kawase is an approximation of Gaussian, but it behaves differently from it.
-    // A radius transformation is required for approximating them, and also to introduce
-    // non-integer steps, necessary to smoothly interpolate large radii.
-    float tmpRadius = (float)blurRadius / 2.0f;
-    float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
-    float radiusByPasses = tmpRadius / (float)numberOfPasses;
-
-    // create blur surface with the bit depth and colorspace of the original surface
-    SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
-                                                       std::ceil(blurRect.height() * kInputScale));
-
-    const float stepX = radiusByPasses;
-    const float stepY = radiusByPasses;
-
-    // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
-    // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
-    // but instead we must do the inverse.
-    SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
-    blurMatrix.postScale(kInputScale, kInputScale);
-
-    // start by downscaling and doing the first blur pass
-    SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
-    SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
-    blurBuilder.child("input") =
-            input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
-    blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
-    blurBuilder.uniform("in_maxSizeXY") =
-            SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
-
-    sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
-
-    // And now we'll build our chain of scaled blur stages
-    for (auto i = 1; i < numberOfPasses; i++) {
-        const float stepScale = (float)i * kInputScale;
-        blurBuilder.child("input") =
-                tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
-        blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
-        blurBuilder.uniform("in_maxSizeXY") =
-                SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
-        tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
-    }
-
-    return tmpBlur;
-}
-
-static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
-    // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect,
+                                   const float scale) {
+    // 1. Apply the blur shader matrix, which scales up the blurred surface to its real size
     auto matrix = SkMatrix::Scale(scale, scale);
     // 2. Since the blurred surface has the size of the layer, we align it with the
     // top left corner of the layer position.
@@ -139,6 +66,14 @@
     return matrix;
 }
 
+BlurFilter::BlurFilter(const float maxCrossFadeRadius)
+      : mMaxCrossFadeRadius(maxCrossFadeRadius),
+        mMixEffect(maxCrossFadeRadius > 0 ? createMixEffect() : nullptr) {}
+
+float BlurFilter::getMaxCrossFadeRadius() const {
+    return mMaxCrossFadeRadius;
+}
+
 void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
                                 const uint32_t blurRadius, const float blurAlpha,
                                 const SkRect& blurRect, sk_sp<SkImage> blurredImage,
@@ -153,7 +88,7 @@
     const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
                                                      linearSampling, &blurMatrix);
 
-    if (blurRadius < kMaxCrossFadeRadius) {
+    if (blurRadius < mMaxCrossFadeRadius) {
         // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
         // case you might expect the matrix to simply be the canvas matrix.
         SkMatrix inputMatrix;
@@ -166,7 +101,7 @@
         blurBuilder.child("originalInput") =
                 input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
                                   inputMatrix);
-        blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
+        blurBuilder.uniform("mixFactor") = blurRadius / mMaxCrossFadeRadius;
 
         paint.setShader(blurBuilder.makeShader(nullptr, true));
     } else {
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 7110018..9cddc75 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -27,29 +27,19 @@
 namespace renderengine {
 namespace skia {
 
-/**
- * This is an implementation of a Kawase blur, as described in here:
- * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
- * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
- */
 class BlurFilter {
 public:
     // Downsample FBO to improve performance
     static constexpr float kInputScale = 0.25f;
     // Downsample scale factor used to improve performance
     static constexpr float kInverseInputScale = 1.0f / kInputScale;
-    // Maximum number of render passes
-    static constexpr uint32_t kMaxPasses = 4;
-    // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
-    // image, up to this radius.
-    static constexpr float kMaxCrossFadeRadius = 10.0f;
 
-    explicit BlurFilter();
-    virtual ~BlurFilter(){};
+    explicit BlurFilter(float maxCrossFadeRadius = 10.0f);
+    virtual ~BlurFilter(){}
 
     // Execute blur, saving it to a texture
-    sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
-                            const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+    virtual sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+                            const sk_sp<SkImage> blurInput, const SkRect& blurRect) const = 0;
 
     /**
      * Draw the blurred content (from the generate method) into the canvas.
@@ -61,13 +51,20 @@
      * @param blurredImage down-sampled blurred content that was produced by the generate() method
      * @param input original unblurred input that is used to crossfade with the blurredImage
      */
-    void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
-                        const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
-                        sk_sp<SkImage> input);
+    void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+                                const uint32_t blurRadius, const float blurAlpha,
+                                const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+                                sk_sp<SkImage> input);
+
+    float getMaxCrossFadeRadius() const;
 
 private:
-    sk_sp<SkRuntimeEffect> mBlurEffect;
-    sk_sp<SkRuntimeEffect> mMixEffect;
+    // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+    // image, up to this radius.
+    const float mMaxCrossFadeRadius;
+
+    // Optional blend used for crossfade only if mMaxCrossFadeRadius > 0
+    const sk_sp<SkRuntimeEffect> mMixEffect;
 };
 
 } // namespace skia
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
new file mode 100644
index 0000000..55867a9
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GaussianBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkImageFilters.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// This constant approximates the scaling done in the software path's
+// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+static const float BLUR_SIGMA_SCALE = 0.57735f;
+
+GaussianBlurFilter::GaussianBlurFilter(): BlurFilter(/* maxCrossFadeRadius= */ 0.0f) {}
+
+sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+                                            const sk_sp<SkImage> input, const SkRect& blurRect)
+    const {
+    // Create blur surface with the bit depth and colorspace of the original surface
+    SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+                                                       std::ceil(blurRect.height() * kInputScale));
+    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, scaledInfo);
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setImageFilter(SkImageFilters::Blur(
+                blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+                blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+                SkTileMode::kClamp, nullptr));
+
+    surface->getCanvas()->drawImageRect(
+            input,
+            blurRect,
+            SkRect::MakeWH(scaledInfo.width(), scaledInfo.height()),
+            SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone},
+            &paint,
+            SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+    return surface->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.h b/libs/renderengine/skia/filters/GaussianBlurFilter.h
new file mode 100644
index 0000000..a4febd2
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Gaussian blur using Skia's built-in GaussianBlur filter.
+ */
+class GaussianBlurFilter: public BlurFilter {
+public:
+    explicit GaussianBlurFilter();
+    virtual ~GaussianBlurFilter(){}
+
+    // Execute blur, saving it to a texture
+    sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+                            const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
new file mode 100644
index 0000000..bfde06f
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
+    SkString blurString(R"(
+        uniform shader child;
+        uniform float in_blurOffset;
+
+        half4 main(float2 xy) {
+            half4 c = child.eval(xy);
+            c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
+            c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
+            c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
+            c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
+            return half4(c.rgb * 0.2, 1.0);
+        }
+    )");
+
+    auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+    if (!blurEffect) {
+        LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+    }
+    mBlurEffect = std::move(blurEffect);
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+                                          const sk_sp<SkImage> input, const SkRect& blurRect)
+    const {
+    // Kawase is an approximation of Gaussian, but it behaves differently from it.
+    // A radius transformation is required for approximating them, and also to introduce
+    // non-integer steps, necessary to smoothly interpolate large radii.
+    float tmpRadius = (float)blurRadius / 2.0f;
+    float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+    float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+    // create blur surface with the bit depth and colorspace of the original surface
+    SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+                                                       std::ceil(blurRect.height() * kInputScale));
+
+    // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+    // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+    // but instead we must do the inverse.
+    SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+    blurMatrix.postScale(kInputScale, kInputScale);
+
+    // start by downscaling and doing the first blur pass
+    SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
+    SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+    blurBuilder.child("child") =
+            input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+    blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
+
+    sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+
+    // And now we'll build our chain of scaled blur stages
+    for (auto i = 1; i < numberOfPasses; i++) {
+        blurBuilder.child("child") =
+                tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+        blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
+        tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+    }
+
+    return tmpBlur;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.h b/libs/renderengine/skia/filters/KawaseBlurFilter.h
new file mode 100644
index 0000000..0ac5ac8
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class KawaseBlurFilter: public BlurFilter {
+public:
+    // Maximum number of render passes
+    static constexpr uint32_t kMaxPasses = 4;
+
+    explicit KawaseBlurFilter();
+    virtual ~KawaseBlurFilter(){}
+
+    // Execute blur, saving it to a texture
+    sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+                            const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+    sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index fc45af9..36305ae 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -19,423 +19,19 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <SkString.h>
+#include <log/log.h>
+#include <shaders/shaders.h>
 #include <utils/Trace.h>
 
-#include <optional>
-
-#include "log/log.h"
-#include "math/mat4.h"
-#include "system/graphics-base-v1.0.h"
-#include "ui/ColorSpace.h"
+#include <math/mat4.h>
 
 namespace android {
 namespace renderengine {
 namespace skia {
 
-static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
-    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
-        case HAL_DATASPACE_TRANSFER_ST2084:
-            shader.append(R"(
-
-                float3 EOTF(float3 color) {
-                    float m1 = (2610.0 / 4096.0) / 4.0;
-                    float m2 = (2523.0 / 4096.0) * 128.0;
-                    float c1 = (3424.0 / 4096.0);
-                    float c2 = (2413.0 / 4096.0) * 32.0;
-                    float c3 = (2392.0 / 4096.0) * 32.0;
-
-                    float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
-                    tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
-                    return pow(tmp, 1.0 / float3(m1));
-                }
-            )");
-            break;
-        case HAL_DATASPACE_TRANSFER_HLG:
-            shader.append(R"(
-                float EOTF_channel(float channel) {
-                    const float a = 0.17883277;
-                    const float b = 0.28466892;
-                    const float c = 0.55991073;
-                    return channel <= 0.5 ? channel * channel / 3.0 :
-                            (exp((channel - c) / a) + b) / 12.0;
-                }
-
-                float3 EOTF(float3 color) {
-                    return float3(EOTF_channel(color.r), EOTF_channel(color.g),
-                            EOTF_channel(color.b));
-                }
-            )");
-            break;
-        case HAL_DATASPACE_TRANSFER_LINEAR:
-            shader.append(R"(
-                float3 EOTF(float3 color) {
-                    return color;
-                }
-            )");
-            break;
-        case HAL_DATASPACE_TRANSFER_SRGB:
-        default:
-            shader.append(R"(
-
-                float EOTF_sRGB(float srgb) {
-                    return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
-                }
-
-                float3 EOTF_sRGB(float3 srgb) {
-                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
-                }
-
-                float3 EOTF(float3 srgb) {
-                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
-                }
-            )");
-            break;
-    }
-}
-
-static void generateXYZTransforms(SkString& shader) {
-    shader.append(R"(
-        uniform float4x4 in_rgbToXyz;
-        uniform float4x4 in_xyzToRgb;
-        float3 ToXYZ(float3 rgb) {
-            return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
-        }
-
-        float3 ToRGB(float3 xyz) {
-            return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
-        }
-    )");
-}
-
-// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
-static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) {
-    switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
-        case HAL_DATASPACE_TRANSFER_ST2084:
-            shader.append(R"(
-                    float3 ScaleLuminance(float3 xyz) {
-                        return xyz * 10000.0;
-                    }
-                )");
-            break;
-        case HAL_DATASPACE_TRANSFER_HLG:
-            shader.append(R"(
-                    float3 ScaleLuminance(float3 xyz) {
-                        return xyz * 1000.0 * pow(xyz.y, 0.2);
-                    }
-                )");
-            break;
-        default:
-            shader.append(R"(
-                    float3 ScaleLuminance(float3 xyz) {
-                        return xyz * in_inputMaxLuminance;
-                    }
-                )");
-            break;
-    }
-}
-
-static void generateToneMapInterpolation(ui::Dataspace inputDataspace,
-                                         ui::Dataspace outputDataspace, SkString& shader) {
-    switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
-        case HAL_DATASPACE_TRANSFER_ST2084:
-        case HAL_DATASPACE_TRANSFER_HLG:
-            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
-                case HAL_DATASPACE_TRANSFER_ST2084:
-                    shader.append(R"(
-                            float3 ToneMap(float3 xyz) {
-                                return xyz;
-                            }
-                        )");
-                    break;
-                case HAL_DATASPACE_TRANSFER_HLG:
-                    // 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.
-                    shader.append(R"(
-                            float3 ToneMap(float3 xyz) {
-                                return clamp(xyz, 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.
-                    shader.append(R"(
-                            float3 ToneMap(float3 xyz) {
-                                float maxInLumi = in_inputMaxLuminance;
-                                float maxOutLumi = in_displayMaxLuminance;
-
-                                float nits = 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 (maxInLumi <= maxOutLumi) {
-                                    return xyz;
-                                } else {
-
-                                    // three control points
-                                    const float x0 = 10.0;
-                                    const float y0 = 17.0;
-                                    float x1 = maxOutLumi * 0.75;
-                                    float y1 = x1;
-                                    float x2 = x1 + (maxInLumi - x1) / 2.0;
-                                    float y2 = y1 + (maxOutLumi - y1) * 0.75;
-
-                                    // horizontal distances between the last three control points
-                                    float h12 = x2 - x1;
-                                    float h23 = maxInLumi - x2;
-                                    // tangents at the last three control points
-                                    float m1 = (y2 - y1) / h12;
-                                    float m3 = (maxOutLumi - y2) / h23;
-                                    float m2 = (m1 + m3) / 2.0;
-
-                                    if (nits < x0) {
-                                        // scale [0.0, x0] to [0.0, y0] linearly
-                                        float slope = y0 / x0;
-                                        return xyz * slope;
-                                    } else if (nits < x1) {
-                                        // scale [x0, x1] to [y0, y1] linearly
-                                        float slope = (y1 - y0) / (x1 - x0);
-                                        nits = y0 + (nits - x0) * slope;
-                                    } else if (nits < x2) {
-                                        // scale [x1, x2] to [y1, y2] using Hermite interp
-                                        float t = (nits - x1) / h12;
-                                        nits = (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
-                                        float t = (nits - x2) / h23;
-                                        nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
-                                                (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
-                                    }
-                                }
-
-                                // color.y is greater than x0 and is thus non-zero
-                                return xyz * (nits / xyz.y);
-                            }
-                        )");
-                    break;
-            }
-            break;
-        default:
-            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
-                case HAL_DATASPACE_TRANSFER_ST2084:
-                case HAL_DATASPACE_TRANSFER_HLG:
-                    // 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.
-                    shader.append(R"(
-                            float3 ToneMap(float3 xyz) {
-                                const float maxOutLumi = 3000.0;
-
-                                const float x0 = 5.0;
-                                const float y0 = 2.5;
-                                float x1 = in_displayMaxLuminance * 0.7;
-                                float y1 = maxOutLumi * 0.15;
-                                float x2 = in_displayMaxLuminance * 0.9;
-                                float y2 = maxOutLumi * 0.45;
-                                float x3 = in_displayMaxLuminance;
-                                float y3 = maxOutLumi;
-
-                                float c1 = y1 / 3.0;
-                                float c2 = y2 / 2.0;
-                                float c3 = y3 / 1.5;
-
-                                float nits = xyz.y;
-
-                                if (nits <= x0) {
-                                    // scale [0.0, x0] to [0.0, y0] linearly
-                                    float slope = y0 / x0;
-                                    return xyz * slope;
-                                } else if (nits <= x1) {
-                                    // scale [x0, x1] to [y0, y1] using a curve
-                                    float t = (nits - x0) / (x1 - x0);
-                                    nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
-                                } else if (nits <= x2) {
-                                    // scale [x1, x2] to [y1, y2] using a curve
-                                    float t = (nits - x1) / (x2 - x1);
-                                    nits = (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
-                                    float t = (nits - x2) / (x3 - x2);
-                                    nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
-                                }
-
-                                // xyz.y is greater than x0 and is thus non-zero
-                                return xyz * (nits / xyz.y);
-                            }
-                        )");
-                    break;
-                default:
-                    // For completeness, this is tone-mapping from SDR to SDR, where this is just a
-                    // no-op.
-                    shader.append(R"(
-                            float3 ToneMap(float3 xyz) {
-                                return xyz;
-                            }
-                        )");
-                    break;
-            }
-            break;
-    }
-}
-
-// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
-static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) {
-    switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
-        case HAL_DATASPACE_TRANSFER_ST2084:
-            shader.append(R"(
-                    float3 NormalizeLuminance(float3 xyz) {
-                        return xyz / 10000.0;
-                    }
-                )");
-            break;
-        case HAL_DATASPACE_TRANSFER_HLG:
-            shader.append(R"(
-                    float3 NormalizeLuminance(float3 xyz) {
-                        return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
-                    }
-                )");
-            break;
-        default:
-            shader.append(R"(
-                    float3 NormalizeLuminance(float3 xyz) {
-                        return xyz / in_displayMaxLuminance;
-                    }
-                )");
-            break;
-    }
-}
-
-static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
-                         SkString& shader) {
-    // Input uniforms
-    shader.append(R"(
-            uniform float in_displayMaxLuminance;
-            uniform float in_inputMaxLuminance;
-        )");
-
-    generateLuminanceScalesForOOTF(inputDataspace, shader);
-    generateToneMapInterpolation(inputDataspace, outputDataspace, shader);
-    generateLuminanceNormalizationForOOTF(outputDataspace, shader);
-
-    shader.append(R"(
-            float3 OOTF(float3 xyz) {
-                return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
-            }
-        )");
-}
-
-static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
-    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
-        case HAL_DATASPACE_TRANSFER_ST2084:
-            shader.append(R"(
-
-                float3 OETF(float3 xyz) {
-                    float m1 = (2610.0 / 4096.0) / 4.0;
-                    float m2 = (2523.0 / 4096.0) * 128.0;
-                    float c1 = (3424.0 / 4096.0);
-                    float c2 = (2413.0 / 4096.0) * 32.0;
-                    float c3 = (2392.0 / 4096.0) * 32.0;
-
-                    float3 tmp = pow(xyz, float3(m1));
-                    tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
-                    return pow(tmp, float3(m2));
-                }
-            )");
-            break;
-        case HAL_DATASPACE_TRANSFER_HLG:
-            shader.append(R"(
-                float OETF_channel(float channel) {
-                    const float a = 0.17883277;
-                    const float b = 0.28466892;
-                    const float c = 0.55991073;
-                    return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
-                            a * log(12.0 * channel - b) + c;
-                }
-
-                float3 OETF(float3 linear) {
-                    return float3(OETF_channel(linear.r), OETF_channel(linear.g),
-                            OETF_channel(linear.b));
-                }
-            )");
-            break;
-        case HAL_DATASPACE_TRANSFER_LINEAR:
-            shader.append(R"(
-                float3 OETF(float3 linear) {
-                    return linear;
-                }
-            )");
-            break;
-        case HAL_DATASPACE_TRANSFER_SRGB:
-        default:
-            shader.append(R"(
-                float OETF_sRGB(float linear) {
-                    return linear <= 0.0031308 ?
-                            linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
-                }
-
-                float3 OETF_sRGB(float3 linear) {
-                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
-                }
-
-                float3 OETF(float3 linear) {
-                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
-                }
-            )");
-            break;
-    }
-}
-
-static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
-    shader.append(R"(
-        uniform shader input;
-        half4 main(float2 xy) {
-            float4 c = float4(sample(input, xy));
-    )");
-    if (undoPremultipliedAlpha) {
-        shader.append(R"(
-            c.rgb = c.rgb / (c.a + 0.0019);
-        )");
-    }
-    shader.append(R"(
-        c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
-    )");
-    if (undoPremultipliedAlpha) {
-        shader.append(R"(
-            c.rgb = c.rgb * (c.a + 0.0019);
-        )");
-    }
-    shader.append(R"(
-            return c;
-        }
-    )");
-}
-static ColorSpace toColorSpace(ui::Dataspace dataspace) {
-    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
-        case HAL_DATASPACE_STANDARD_BT709:
-            return ColorSpace::sRGB();
-            break;
-        case HAL_DATASPACE_STANDARD_DCI_P3:
-            return ColorSpace::DisplayP3();
-            break;
-        case HAL_DATASPACE_STANDARD_BT2020:
-            return ColorSpace::BT2020();
-            break;
-        default:
-            return ColorSpace::sRGB();
-            break;
-    }
-}
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) {
     ATRACE_CALL();
-    SkString shaderString;
-    generateEOTF(linearEffect.inputDataspace, shaderString);
-    generateXYZTransforms(shaderString);
-    generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
-    generateOETF(linearEffect.outputDataspace, shaderString);
-    generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+    SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
 
     auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
     if (!shader) {
@@ -444,32 +40,23 @@
     return shader;
 }
 
-sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
+                                         const shaders::LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
                                          float maxLuminance) {
     ATRACE_CALL();
     SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
 
-    effectBuilder.child("input") = shader;
+    effectBuilder.child("child") = shader;
 
-    if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
-        effectBuilder.uniform("in_rgbToXyz") = mat4();
-        effectBuilder.uniform("in_xyzToRgb") = colorTransform;
-    } else {
-        ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
-        ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+    const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform,
+                                                             maxDisplayLuminance, maxLuminance);
 
-        effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
-        effectBuilder.uniform("in_xyzToRgb") =
-                colorTransform * mat4(outputColorSpace.getXYZtoRGB());
+    for (const auto& uniform : uniforms) {
+        effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
     }
 
-    effectBuilder.uniform("in_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
-    effectBuilder.uniform("in_inputMaxLuminance") =
-            maxLuminance > 0 ? maxLuminance : maxDisplayLuminance;
     return effectBuilder.makeShader(nullptr, false);
 }
 
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 14a3b61..8eb6670 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -20,6 +20,7 @@
 
 #include <optional>
 
+#include <shaders/shaders.h>
 #include "SkRuntimeEffect.h"
 #include "SkShader.h"
 #include "ui/GraphicTypes.h"
@@ -28,61 +29,7 @@
 namespace renderengine {
 namespace skia {
 
-/**
- * Arguments for creating an effect that applies color transformations in linear XYZ space.
- * A linear effect is decomposed into the following steps when operating on an image:
- * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
- * relative display brightness of the scene in nits for each RGB channel
- * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
- * luminance.
- * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
- * mapping to display SDR content alongside HDR content, or any number of subjective transformations
- * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
- * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
- * output RGB colors.
- *
- * For further reading, consult the recommendation in ITU-R BT.2390-4:
- * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
- *
- * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
- * intended to be the output surface. However, Skia does not support complex tone mapping such as
- * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
- * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
- * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
- * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
- * and intermediate values are interpolated.
- */
-struct LinearEffect {
-    // Input dataspace of the source colors.
-    const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
-
-    // Working dataspace for the output surface, for conversion from linear space.
-    const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
-
-    // Sets whether alpha premultiplication must be undone.
-    // This is required if the source colors use premultiplied alpha and is not opaque.
-    const bool undoPremultipliedAlpha = false;
-};
-
-static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
-    return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
-            lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha;
-}
-
-struct LinearEffectHasher {
-    // Inspired by art/runtime/class_linker.cc
-    // Also this is what boost:hash_combine does
-    static size_t HashCombine(size_t seed, size_t val) {
-        return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
-    }
-    size_t operator()(const LinearEffect& le) const {
-        size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
-        result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
-        return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
-    }
-};
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect);
 
 // Generates a shader resulting from applying the a linear effect created from
 // LinearEffectArgs::buildEffect to an inputShader.
@@ -93,7 +40,7 @@
 // * 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.
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
-                                         const LinearEffect& linearEffect,
+                                         const shaders::LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
                                          float maxLuminance);
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
index 4ac5c40..c262e35 100644
--- a/libs/renderengine/skia/filters/StretchShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -184,7 +184,7 @@
         );
         coord.x = (outU - uScrollX) * viewportWidth;
         coord.y = (outV - uScrollY) * viewportHeight;
-        return sample(uContentTexture, coord);
+        return uContentTexture.eval(coord);
     })");
 
 const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index d0e19dd..a426850 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -23,7 +23,10 @@
 
 cc_test {
     name: "librenderengine_test",
-    defaults: ["skia_deps", "surfaceflinger_defaults"],
+    defaults: [
+        "skia_deps",
+        "surfaceflinger_defaults",
+    ],
     test_suites: ["device-tests"],
     srcs: [
         "RenderEngineTest.cpp",
@@ -36,6 +39,8 @@
         "libgmock",
         "librenderengine",
         "librenderengine_mocks",
+        "libshaders",
+        "libtonemap",
     ],
 
     shared_libs: [
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 33e3773..eb2b2dc 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -27,6 +27,9 @@
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
+#include <system/graphics-base-v1.0.h>
+#include <tonemap/tonemap.h>
+#include <ui/ColorSpace.h>
 #include <ui/PixelFormat.h>
 
 #include <chrono>
@@ -205,6 +208,21 @@
                                                  renderengine::ExternalTexture::Usage::WRITEABLE);
     }
 
+    std::shared_ptr<renderengine::ExternalTexture> allocateAndFillSourceBuffer(uint32_t width,
+                                                                               uint32_t height,
+                                                                               ubyte4 color) {
+        const auto buffer = allocateSourceBuffer(width, height);
+        uint8_t* pixels;
+        buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                  reinterpret_cast<void**>(&pixels));
+        pixels[0] = color.r;
+        pixels[1] = color.g;
+        pixels[2] = color.b;
+        pixels[3] = color.a;
+        buffer->getBuffer()->unlock();
+        return buffer;
+    }
+
     RenderEngineTest() {
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
@@ -282,6 +300,13 @@
 
     void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
                            uint8_t tolerance = 0) {
+        auto generator = [=](Point) { return ubyte4(r, g, b, a); };
+        expectBufferColor(rect, generator, tolerance);
+    }
+
+    using ColorGenerator = std::function<ubyte4(Point location)>;
+
+    void expectBufferColor(const Rect& rect, ColorGenerator generator, uint8_t tolerance = 0) {
         auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
             auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
                 uint8_t tmp = a >= b ? a - b : b - a;
@@ -290,10 +315,10 @@
             return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
         };
 
-        expectBufferColor(rect, r, g, b, a, colorCompare);
+        expectBufferColor(rect, generator, colorCompare);
     }
 
-    void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+    void expectBufferColor(const Rect& region, ColorGenerator generator,
                            std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
         uint8_t* pixels;
         mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -304,19 +329,22 @@
             const uint8_t* src = pixels +
                     (mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4;
             for (int32_t i = 0; i < region.getWidth(); i++) {
-                const uint8_t expected[4] = {r, g, b, a};
-                bool equal = colorCompare(src, expected);
-                EXPECT_TRUE(equal)
+                const auto location = Point(region.left + i, region.top + j);
+                const ubyte4 colors = generator(location);
+                const uint8_t expected[4] = {colors.r, colors.g, colors.b, colors.a};
+                bool colorMatches = colorCompare(src, expected);
+                EXPECT_TRUE(colorMatches)
                         << GetParam()->name().c_str() << ": "
-                        << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
-                        << "expected (" << static_cast<uint32_t>(r) << ", "
-                        << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
-                        << static_cast<uint32_t>(a) << "), "
+                        << "pixel @ (" << location.x << ", " << location.y << "): "
+                        << "expected (" << static_cast<uint32_t>(colors.r) << ", "
+                        << static_cast<uint32_t>(colors.g) << ", "
+                        << static_cast<uint32_t>(colors.b) << ", "
+                        << static_cast<uint32_t>(colors.a) << "), "
                         << "got (" << static_cast<uint32_t>(src[0]) << ", "
                         << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2])
                         << ", " << static_cast<uint32_t>(src[3]) << ")";
                 src += 4;
-                if (!equal && ++fails >= maxFails) {
+                if (!colorMatches && ++fails >= maxFails) {
                     break;
                 }
             }
@@ -328,10 +356,11 @@
     }
 
     void expectAlpha(const Rect& rect, uint8_t a) {
+        auto generator = [=](Point) { return ubyte4(0, 0, 0, a); };
         auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
             return colorA[3] == colorB[3];
         };
-        expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+        expectBufferColor(rect, generator, colorCompare);
     }
 
     void expectShadowColor(const renderengine::LayerSettings& castingLayer,
@@ -417,19 +446,19 @@
                     DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
     }
 
-    void invokeDraw(renderengine::DisplaySettings settings,
-                    std::vector<const renderengine::LayerSettings*> layers) {
-        base::unique_fd fence;
-        status_t status =
-                mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+    void invokeDraw(const renderengine::DisplaySettings& settings,
+                    const std::vector<renderengine::LayerSettings>& layers) {
+        std::future<renderengine::RenderEngineResult> result =
+                mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
 
-        int fd = fence.release();
-        if (fd >= 0) {
-            sync_wait(fd, -1);
-            close(fd);
-        }
+        ASSERT_TRUE(result.valid());
+        auto [status, fence] = result.get();
 
         ASSERT_EQ(NO_ERROR, status);
+        if (fence.ok()) {
+            sync_wait(fence.get(), -1);
+        }
+
         if (layers.size() > 0 && mGLESRE != nullptr) {
             ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
         }
@@ -437,7 +466,7 @@
 
     void drawEmptyLayers() {
         renderengine::DisplaySettings settings;
-        std::vector<const renderengine::LayerSettings*> layers;
+        std::vector<renderengine::LayerSettings> layers;
         invokeDraw(settings, layers);
     }
 
@@ -490,6 +519,18 @@
     void fillBufferColorTransform();
 
     template <typename SourceVariant>
+    void fillBufferWithColorTransformAndSourceDataspace(const ui::Dataspace sourceDataspace);
+
+    template <typename SourceVariant>
+    void fillBufferColorTransformAndSourceDataspace();
+
+    template <typename SourceVariant>
+    void fillBufferWithColorTransformAndOutputDataspace(const ui::Dataspace outputDataspace);
+
+    template <typename SourceVariant>
+    void fillBufferColorTransformAndOutputDataspace();
+
+    template <typename SourceVariant>
     void fillBufferWithColorTransformZeroLayerAlpha();
 
     template <typename SourceVariant>
@@ -524,10 +565,6 @@
 
     void fillGreenColorBufferThenClearRegion();
 
-    void clearLeftRegion();
-
-    void clearRegion();
-
     template <typename SourceVariant>
     void drawShadow(const renderengine::LayerSettings& castingLayer,
                     const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
@@ -634,7 +671,7 @@
     settings.clip = fullscreenRect();
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -642,7 +679,7 @@
     SourceVariant::fillColor(layer, r, g, b, this);
     layer.alpha = a;
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -678,7 +715,7 @@
     settings.physicalDisplay = offsetRect();
     settings.clip = offsetRectAtZero();
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -686,7 +723,7 @@
     SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0f;
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
     invokeDraw(settings, layers);
 }
 
@@ -713,7 +750,7 @@
     settings.clip = Rect(2, 2);
     settings.orientation = orientationFlag;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layerOne;
     layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -736,9 +773,9 @@
     SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
     layerThree.alpha = 1.0f;
 
-    layers.push_back(&layerOne);
-    layers.push_back(&layerTwo);
-    layers.push_back(&layerThree);
+    layers.push_back(layerOne);
+    layers.push_back(layerTwo);
+    layers.push_back(layerThree);
 
     invokeDraw(settings, layers);
 }
@@ -815,7 +852,7 @@
     settings.clip = Rect(2, 2);
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -826,7 +863,7 @@
     layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
     layer.alpha = 1.0f;
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -848,7 +885,7 @@
     settings.clip = Rect(1, 1);
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -865,7 +902,37 @@
     layer.alpha = 1.0f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndSourceDataspace(
+        const ui::Dataspace sourceDataspace) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = Rect(1, 1);
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.sourceDataspace = sourceDataspace;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+    layer.alpha = 1.0f;
+
+    // construct a fake color matrix
+    // annihilate green and blue channels
+    settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+    // set red channel to red + green
+    layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+    layer.alpha = 1.0f;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -877,12 +944,74 @@
 }
 
 template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndSourceDataspace() {
+    unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+    dataspaceToColorMap[ui::Dataspace::V0_BT709] = {172, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::BT2020] = {172, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {172, 0, 0, 255};
+    ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+            ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_2 |
+            ui::Dataspace::RANGE_FULL);
+    dataspaceToColorMap[customizedDataspace] = {172, 0, 0, 255};
+    for (const auto& [sourceDataspace, color] : dataspaceToColorMap) {
+        fillBufferWithColorTransformAndSourceDataspace<SourceVariant>(sourceDataspace);
+        expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+    }
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndOutputDataspace(
+        const ui::Dataspace outputDataspace) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = Rect(1, 1);
+    settings.outputDataspace = outputDataspace;
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+    layer.alpha = 1.0f;
+
+    // construct a fake color matrix
+    // annihilate green and blue channels
+    settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+    // set red channel to red + green
+    layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+    layer.alpha = 1.0f;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndOutputDataspace() {
+    unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+    dataspaceToColorMap[ui::Dataspace::V0_BT709] = {202, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::BT2020] = {192, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {202, 0, 0, 255};
+    ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+            ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_6 |
+            ui::Dataspace::RANGE_FULL);
+    dataspaceToColorMap[customizedDataspace] = {202, 0, 0, 255};
+    for (const auto& [outputDataspace, color] : dataspaceToColorMap) {
+        fillBufferWithColorTransformAndOutputDataspace<SourceVariant>(outputDataspace);
+        expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+    }
+}
+
+template <typename SourceVariant>
 void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = Rect(1, 1);
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -895,7 +1024,7 @@
 
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -913,7 +1042,7 @@
     settings.clip = fullscreenRect();
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -923,7 +1052,7 @@
     SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0f;
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -954,14 +1083,14 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings backgroundLayer;
     backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
     SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
     backgroundLayer.alpha = 1.0f;
-    layers.push_back(&backgroundLayer);
+    layers.emplace_back(backgroundLayer);
 
     renderengine::LayerSettings leftLayer;
     leftLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -969,7 +1098,7 @@
             Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
     SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
     leftLayer.alpha = 1.0f;
-    layers.push_back(&leftLayer);
+    layers.emplace_back(leftLayer);
 
     renderengine::LayerSettings blurLayer;
     blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -977,7 +1106,7 @@
     blurLayer.backgroundBlurRadius = blurRadius;
     SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
     blurLayer.alpha = 0;
-    layers.push_back(&blurLayer);
+    layers.emplace_back(blurLayer);
 
     invokeDraw(settings, layers);
 
@@ -999,14 +1128,14 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings backgroundLayer;
     backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
     SourceVariant::fillColor(backgroundLayer, 1.0f, 0.0f, 0.0f, this);
     backgroundLayer.alpha = 1.0f;
-    layers.push_back(&backgroundLayer);
+    layers.push_back(backgroundLayer);
 
     renderengine::LayerSettings blurLayer;
     blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1014,7 +1143,7 @@
     blurLayer.backgroundBlurRadius = blurRadius;
     SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
     blurLayer.alpha = 0;
-    layers.push_back(&blurLayer);
+    layers.push_back(blurLayer);
 
     invokeDraw(settings, layers);
 
@@ -1031,7 +1160,7 @@
     settings.clip = fullscreenRect();
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layersFirst;
+    std::vector<renderengine::LayerSettings> layersFirst;
 
     renderengine::LayerSettings layerOne;
     layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1040,14 +1169,14 @@
     SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
     layerOne.alpha = 0.2;
 
-    layersFirst.push_back(&layerOne);
+    layersFirst.push_back(layerOne);
     invokeDraw(settings, layersFirst);
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
                            DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                       0, 0, 0, 0);
 
-    std::vector<const renderengine::LayerSettings*> layersSecond;
+    std::vector<renderengine::LayerSettings> layersSecond;
     renderengine::LayerSettings layerTwo;
     layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layerTwo.geometry.boundaries =
@@ -1056,7 +1185,7 @@
     SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
     layerTwo.alpha = 1.0f;
 
-    layersSecond.push_back(&layerTwo);
+    layersSecond.push_back(layerTwo);
     invokeDraw(settings, layersSecond);
 
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
@@ -1071,7 +1200,7 @@
     settings.clip = Rect(1, 1);
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1103,11 +1232,11 @@
     layer.source.buffer.buffer = buf;
     layer.source.buffer.textureName = texName;
     // Transform coordinates to only be inside the red quadrant.
-    layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1));
+    layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f));
     layer.alpha = 1.0f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -1123,7 +1252,7 @@
     // Here logical space is 1x1
     settings.clip = Rect(1, 1);
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     const auto buf = allocateSourceBuffer(1, 1);
@@ -1146,7 +1275,7 @@
     layer.alpha = 0.5f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -1162,7 +1291,7 @@
     // Here logical space is 1x1
     settings.clip = Rect(1, 1);
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings layer;
     const auto buf = allocateSourceBuffer(1, 1);
@@ -1185,7 +1314,7 @@
     layer.alpha = 0.5f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -1195,28 +1324,6 @@
     expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
 }
 
-void RenderEngineTest::clearLeftRegion() {
-    renderengine::DisplaySettings settings;
-    settings.physicalDisplay = fullscreenRect();
-    // Here logical space is 4x4
-    settings.clip = Rect(4, 4);
-    settings.clearRegion = Region(Rect(2, 4));
-    std::vector<const renderengine::LayerSettings*> layers;
-    // fake layer, without bounds should not render anything
-    renderengine::LayerSettings layer;
-    layers.push_back(&layer);
-    invokeDraw(settings, layers);
-}
-
-void RenderEngineTest::clearRegion() {
-    // Reuse mBuffer
-    clearLeftRegion();
-    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
-    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
-                           DEFAULT_DISPLAY_HEIGHT),
-                      0, 0, 0, 0);
-}
-
 template <typename SourceVariant>
 void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
                                   const renderengine::ShadowSettings& shadow,
@@ -1226,7 +1333,7 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     // add background layer
     renderengine::LayerSettings bgLayer;
@@ -1235,7 +1342,7 @@
     ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
                                   backgroundColor.b / 255.0f, this);
     bgLayer.alpha = backgroundColor.a / 255.0f;
-    layers.push_back(&bgLayer);
+    layers.push_back(bgLayer);
 
     // add shadow layer
     renderengine::LayerSettings shadowLayer;
@@ -1243,14 +1350,14 @@
     shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
     shadowLayer.alpha = castingLayer.alpha;
     shadowLayer.shadow = shadow;
-    layers.push_back(&shadowLayer);
+    layers.push_back(shadowLayer);
 
     // add layer casting the shadow
     renderengine::LayerSettings layer = castingLayer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
                              casterColor.b / 255.0f, this);
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
     invokeDraw(settings, layers);
 }
@@ -1263,7 +1370,7 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     // add background layer
     renderengine::LayerSettings bgLayer;
@@ -1272,7 +1379,7 @@
     ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
                                   backgroundColor.b / 255.0f, this);
     bgLayer.alpha = backgroundColor.a / 255.0f;
-    layers.push_back(&bgLayer);
+    layers.push_back(bgLayer);
 
     // add shadow layer
     renderengine::LayerSettings shadowLayer;
@@ -1282,7 +1389,7 @@
     shadowLayer.alpha = 1.0f;
     ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
     shadowLayer.shadow = shadow;
-    layers.push_back(&shadowLayer);
+    layers.push_back(shadowLayer);
 
     invokeDraw(settings, layers);
 }
@@ -1307,7 +1414,8 @@
     settings.clip = fullscreenRect();
 
     // 255, 255, 255, 255 is full opaque white.
-    const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f);
+    const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+                                 static_cast<uint8_t>(255), static_cast<uint8_t>(255));
     // Create layer with given color.
     renderengine::LayerSettings bgLayer;
     bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1318,8 +1426,8 @@
     // Transform the red color.
     bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
 
-    std::vector<const renderengine::LayerSettings*> layers;
-    layers.push_back(&bgLayer);
+    std::vector<renderengine::LayerSettings> layers;
+    layers.push_back(bgLayer);
 
     invokeDraw(settings, layers);
 
@@ -1333,35 +1441,18 @@
 
     renderengine::DisplaySettings settings;
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
-    layers.push_back(&layer);
-    base::unique_fd fence;
-    status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
+    layers.push_back(layer);
+    std::future<renderengine::RenderEngineResult> result =
+            mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
 
+    ASSERT_TRUE(result.valid());
+    auto [status, fence] = result.get();
     ASSERT_EQ(BAD_VALUE, status);
-}
-
-TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
-    initializeRenderEngine();
-
-    renderengine::DisplaySettings settings;
-    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-    settings.physicalDisplay = fullscreenRect();
-    settings.clip = fullscreenRect();
-
-    std::vector<const renderengine::LayerSettings*> layers;
-    renderengine::LayerSettings layer;
-    layer.geometry.boundaries = fullscreenRect().toFloatRect();
-    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
-    layer.alpha = 1.0;
-    layers.push_back(&layer);
-
-    status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
-    ASSERT_EQ(NO_ERROR, status);
-    expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+    ASSERT_FALSE(fence.ok());
 }
 
 TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
@@ -1379,15 +1470,23 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0;
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
-    status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
+    std::future<renderengine::RenderEngineResult> result =
+            mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd());
+    ASSERT_TRUE(result.valid());
+    auto [status, fence] = result.get();
+
     ASSERT_EQ(NO_ERROR, status);
+    if (fence.ok()) {
+        sync_wait(fence.get(), -1);
+    }
+
     ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
@@ -1447,6 +1546,36 @@
     fillBufferColorTransform<ColorSourceVariant>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
+    const auto& renderEngineFactory = GetParam();
+    // skip for non color management
+    if (!renderEngineFactory->useColorManagement()) {
+        return;
+    }
+    // skip for GLESRenderEngine
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+    fillBufferColorTransformAndSourceDataspace<ColorSourceVariant>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
+    const auto& renderEngineFactory = GetParam();
+    // skip for non color management
+    if (!renderEngineFactory->useColorManagement()) {
+        return;
+    }
+    // skip for GLESRenderEngine
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+    fillBufferColorTransformAndOutputDataspace<ColorSourceVariant>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
     initializeRenderEngine();
     fillBufferWithRoundedCorners<ColorSourceVariant>();
@@ -1527,6 +1656,36 @@
     fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    // skip for non color management
+    if (!renderEngineFactory->useColorManagement()) {
+        return;
+    }
+    // skip for GLESRenderEngine
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+    fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    // skip for non color management
+    if (!renderEngineFactory->useColorManagement()) {
+        return;
+    }
+    // skip for GLESRenderEngine
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+    fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
     initializeRenderEngine();
     fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
@@ -1607,6 +1766,36 @@
     fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    // skip for non color management
+    if (!renderEngineFactory->useColorManagement()) {
+        return;
+    }
+    // skip for GLESRenderEngine
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+    fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    // skip for non color management
+    if (!renderEngineFactory->useColorManagement()) {
+        return;
+    }
+    // skip for GLESRenderEngine
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+    fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
     initializeRenderEngine();
     fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
@@ -1647,15 +1836,11 @@
     fillBufferWithoutPremultiplyAlpha();
 }
 
-TEST_P(RenderEngineTest, drawLayers_clearRegion) {
-    initializeRenderEngine();
-    clearRegion();
-}
-
 TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
     initializeRenderEngine();
 
-    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+                                 static_cast<uint8_t>(255), static_cast<uint8_t>(255));
     const float shadowLength = 5.0f;
     Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1670,8 +1855,10 @@
 TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
     initializeRenderEngine();
 
-    const ubyte4 casterColor(255, 0, 0, 255);
-    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+                             static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+    const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+                                 static_cast<uint8_t>(255), static_cast<uint8_t>(255));
     const float shadowLength = 5.0f;
     Rect casterBounds(1, 1);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1689,8 +1876,10 @@
 TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
     initializeRenderEngine();
 
-    const ubyte4 casterColor(255, 0, 0, 255);
-    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+                             static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+    const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+                                 static_cast<uint8_t>(255), static_cast<uint8_t>(255));
     const float shadowLength = 5.0f;
     Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1709,8 +1898,10 @@
 TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
     initializeRenderEngine();
 
-    const ubyte4 casterColor(255, 0, 0, 255);
-    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+                             static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+    const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+                                 static_cast<uint8_t>(255), static_cast<uint8_t>(255));
     const float shadowLength = 5.0f;
     Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1730,8 +1921,10 @@
 TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
     initializeRenderEngine();
 
-    const ubyte4 casterColor(255, 0, 0, 255);
-    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+                             static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+    const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+                                 static_cast<uint8_t>(255), static_cast<uint8_t>(255));
     const float shadowLength = 5.0f;
     Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1784,22 +1977,28 @@
     settings.clip = fullscreenRect();
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0;
-    layers.push_back(&layer);
+    layers.push_back(layer);
 
-    base::unique_fd fenceOne;
-    mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
-    base::unique_fd fenceTwo;
-    mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
+    std::future<renderengine::RenderEngineResult> resultOne =
+            mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+    ASSERT_TRUE(resultOne.valid());
+    auto [statusOne, fenceOne] = resultOne.get();
+    ASSERT_EQ(NO_ERROR, statusOne);
 
-    const int fd = fenceTwo.get();
-    if (fd >= 0) {
-        sync_wait(fd, -1);
+    std::future<renderengine::RenderEngineResult> resultTwo =
+            mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne));
+    ASSERT_TRUE(resultTwo.valid());
+    auto [statusTwo, fenceTwo] = resultTwo.get();
+    ASSERT_EQ(NO_ERROR, statusTwo);
+    if (fenceTwo.ok()) {
+        sync_wait(fenceTwo.get(), -1);
     }
+
     // Only cleanup the first time.
     EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
     mRE->cleanupPostRender();
@@ -1814,7 +2013,7 @@
     settings.clip = fullscreenRect();
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1825,7 +2024,7 @@
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
     redLayer.alpha = 1.0f;
 
-    layers.push_back(&redLayer);
+    layers.push_back(redLayer);
 
     // Green layer with 1/3 size.
     renderengine::LayerSettings greenLayer;
@@ -1840,7 +2039,7 @@
     greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
     greenLayer.alpha = 1.0f;
 
-    layers.push_back(&greenLayer);
+    layers.push_back(greenLayer);
 
     invokeDraw(settings, layers);
 
@@ -1863,7 +2062,7 @@
     settings.clip = fullscreenRect();
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
 
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1874,7 +2073,7 @@
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
     redLayer.alpha = 1.0f;
 
-    layers.push_back(&redLayer);
+    layers.push_back(redLayer);
 
     // Green layer with 1/2 size with parent crop rect.
     renderengine::LayerSettings greenLayer = redLayer;
@@ -1882,7 +2081,7 @@
             FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
     greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
 
-    layers.push_back(&greenLayer);
+    layers.push_back(greenLayer);
 
     invokeDraw(settings, layers);
 
@@ -1900,6 +2099,40 @@
     expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
 }
 
+TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
+    initializeRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings redLayer;
+    redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
+    redLayer.geometry.roundedCornersRadius = 64;
+    redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
+    // Red background.
+    redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+    redLayer.alpha = 1.0f;
+
+    layers.push_back(redLayer);
+    invokeDraw(settings, layers);
+
+    // Due to roundedCornersRadius, the top corners are untouched.
+    expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+
+    // ensure that the entire height of the red layer was clipped by the rounded corners crop.
+    expectBufferColor(Point(0, 31), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0);
+
+    // the bottom middle should be red
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
+}
+
 TEST_P(RenderEngineTest, testClear) {
     initializeRenderEngine();
 
@@ -1924,7 +2157,7 @@
             .disableBlending = true,
     };
 
-    std::vector<const renderengine::LayerSettings*> layers{&redLayer, &clearLayer};
+    std::vector<renderengine::LayerSettings> layers{redLayer, clearLayer};
     invokeDraw(display, layers);
     expectBufferColor(rect, 0, 0, 0, 0);
 }
@@ -1972,11 +2205,138 @@
             .disableBlending = true,
     };
 
-    std::vector<const renderengine::LayerSettings*> layers{&redLayer, &greenLayer};
+    std::vector<renderengine::LayerSettings> layers{redLayer, greenLayer};
     invokeDraw(display, layers);
     expectBufferColor(rect, 0, 128, 0, 128);
 }
 
+TEST_P(RenderEngineTest, testDimming) {
+    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+    initializeRenderEngine();
+
+    const auto displayRect = Rect(3, 1);
+    const renderengine::DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+            .targetLuminanceNits = 1000.f,
+    };
+
+    const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+    const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+    const auto redBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(255, 0, 0, 255));
+
+    const renderengine::LayerSettings greenLayer{
+            .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = greenBuffer,
+                                            .usePremultipliedAlpha = true,
+                                    },
+                    },
+            .alpha = 1.0f,
+            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+            .whitePointNits = 200.f,
+    };
+
+    const renderengine::LayerSettings blueLayer{
+            .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = blueBuffer,
+                                            .usePremultipliedAlpha = true,
+                                    },
+                    },
+            .alpha = 1.0f,
+            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+            .whitePointNits = 1000.f / 51.f,
+    };
+
+    const renderengine::LayerSettings redLayer{
+            .geometry.boundaries = FloatRect(2.f, 0.f, 3.f, 1.f),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = redBuffer,
+                                            .usePremultipliedAlpha = true,
+                                    },
+                    },
+            .alpha = 1.0f,
+            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+            // When the white point is not set for a layer, just ignore it and treat it as the same
+            // as the max layer
+            .whitePointNits = -1.f,
+    };
+
+    std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer, redLayer};
+    invokeDraw(display, layers);
+
+    expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+    expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 5, 255, 1);
+    expectBufferColor(Rect(2, 0, 3, 1), 51, 0, 0, 255, 1);
+}
+
+TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
+    initializeRenderEngine();
+    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    const auto displayRect = Rect(2, 1);
+    const renderengine::DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+            .targetLuminanceNits = -1.f,
+    };
+
+    const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+    const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+
+    const renderengine::LayerSettings greenLayer{
+            .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = greenBuffer,
+                                            .usePremultipliedAlpha = true,
+                                    },
+                    },
+            .alpha = 1.0f,
+            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+            .whitePointNits = 200.f,
+    };
+
+    const renderengine::LayerSettings blueLayer{
+            .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = blueBuffer,
+                                            .usePremultipliedAlpha = true,
+                                    },
+                    },
+            .alpha = 1.0f,
+            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+            .whitePointNits = 1000.f,
+    };
+
+    std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer};
+    invokeDraw(display, layers);
+
+    expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+    expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 255, 255);
+}
+
 TEST_P(RenderEngineTest, test_isOpaque) {
     initializeRenderEngine();
 
@@ -2018,7 +2378,7 @@
             .alpha = 1.0f,
     };
 
-    std::vector<const renderengine::LayerSettings*> layers{&greenLayer};
+    std::vector<renderengine::LayerSettings> layers{greenLayer};
     invokeDraw(display, layers);
 
     if (GetParam()->useColorManagement()) {
@@ -2027,6 +2387,155 @@
         expectBufferColor(rect, 0, 255, 0, 255);
     }
 }
+
+double EOTF_PQ(double channel) {
+    float m1 = (2610.0 / 4096.0) / 4.0;
+    float m2 = (2523.0 / 4096.0) * 128.0;
+    float c1 = (3424.0 / 4096.0);
+    float c2 = (2413.0 / 4096.0) * 32.0;
+    float c3 = (2392.0 / 4096.0) * 32.0;
+
+    float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2);
+    tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp);
+    return std::pow(tmp, 1.0 / m1);
+}
+
+vec3 EOTF_PQ(vec3 color) {
+    return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b));
+}
+
+double OETF_sRGB(double channel) {
+    return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055;
+}
+
+int sign(float in) {
+    return in >= 0.0 ? 1 : -1;
+}
+
+vec3 OETF_sRGB(vec3 linear) {
+    return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g),
+                sign(linear.b) * OETF_sRGB(linear.b));
+}
+
+TEST_P(RenderEngineTest, test_tonemapPQMatches) {
+    if (!GetParam()->useColorManagement()) {
+        return;
+    }
+
+    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+
+    constexpr int32_t kGreyLevels = 256;
+
+    const auto rect = Rect(0, 0, kGreyLevels, 1);
+    const renderengine::DisplaySettings display{
+            .physicalDisplay = rect,
+            .clip = rect,
+            .maxLuminance = 750.0f,
+            .outputDataspace = ui::Dataspace::DISPLAY_P3,
+    };
+
+    auto buf = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
+                                                             HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                             GRALLOC_USAGE_SW_READ_OFTEN |
+                                                                     GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                                                     GRALLOC_USAGE_HW_RENDER |
+                                                                     GRALLOC_USAGE_HW_TEXTURE,
+                                                             "input"),
+                                           *mRE,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+    ASSERT_EQ(0, buf->getBuffer()->initCheck());
+
+    {
+        uint8_t* pixels;
+        buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                               reinterpret_cast<void**>(&pixels));
+
+        uint8_t color = 0;
+        for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) {
+            uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4);
+            for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) {
+                dest[0] = color;
+                dest[1] = color;
+                dest[2] = color;
+                dest[3] = 255;
+                color++;
+                dest += 4;
+            }
+        }
+        buf->getBuffer()->unlock();
+    }
+
+    mBuffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
+                                                             HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                             GRALLOC_USAGE_SW_READ_OFTEN |
+                                                                     GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                                                     GRALLOC_USAGE_HW_RENDER |
+                                                                     GRALLOC_USAGE_HW_TEXTURE,
+                                                             "output"),
+                                           *mRE,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+    ASSERT_EQ(0, mBuffer->getBuffer()->initCheck());
+
+    const renderengine::LayerSettings layer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = std::move(buf),
+                                            .usePremultipliedAlpha = true,
+                                    },
+                    },
+            .alpha = 1.0f,
+            .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+                                                          HAL_DATASPACE_TRANSFER_ST2084 |
+                                                          HAL_DATASPACE_RANGE_FULL),
+    };
+
+    std::vector<renderengine::LayerSettings> layers{layer};
+    invokeDraw(display, layers);
+
+    ColorSpace displayP3 = ColorSpace::DisplayP3();
+    ColorSpace bt2020 = ColorSpace::BT2020();
+
+    tonemap::Metadata metadata{.displayMaxLuminance = 750.0f};
+
+    auto generator = [=](Point location) {
+        const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1);
+        const vec3 rgb = vec3(normColor, normColor, normColor);
+
+        const vec3 linearRGB = EOTF_PQ(rgb);
+
+        static constexpr float kMaxPQLuminance = 10000.f;
+        const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance;
+        const double gain =
+                tonemap::getToneMapper()
+                        ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common::
+                                                                Dataspace>(
+                                                    HAL_DATASPACE_STANDARD_BT2020 |
+                                                    HAL_DATASPACE_TRANSFER_ST2084 |
+                                                    HAL_DATASPACE_RANGE_FULL),
+                                            static_cast<aidl::android::hardware::graphics::common::
+                                                                Dataspace>(
+                                                    ui::Dataspace::DISPLAY_P3),
+                                            linearRGB * 10000.0, xyz, metadata);
+        const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance;
+
+        const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255;
+        return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g),
+                      static_cast<uint8_t>(targetRGB.b), 255);
+    };
+
+    expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
+}
 } // namespace renderengine
 } // namespace android
 
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 830f463..db7e12b 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -169,25 +169,32 @@
     status_t result = mThreadedRE->supportsBackgroundBlur();
     ASSERT_EQ(true, result);
 }
+
 TEST_F(RenderEngineThreadedTest, drawLayers) {
     renderengine::DisplaySettings settings;
-    std::vector<const renderengine::LayerSettings*> layers;
+    std::vector<renderengine::LayerSettings> layers;
     std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
             renderengine::ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
                                            renderengine::ExternalTexture::Usage::READABLE |
                                                    renderengine::ExternalTexture::Usage::WRITEABLE);
+
     base::unique_fd bufferFence;
-    base::unique_fd drawFence;
 
-    EXPECT_CALL(*mRenderEngine, drawLayers)
-            .WillOnce([](const renderengine::DisplaySettings&,
-                         const std::vector<const renderengine::LayerSettings*>&,
-                         const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                         base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; });
+    EXPECT_CALL(*mRenderEngine, drawLayersInternal)
+            .WillOnce([&](const std::shared_ptr<std::promise<renderengine::RenderEngineResult>>&&
+                                  resultPromise,
+                          const renderengine::DisplaySettings&,
+                          const std::vector<renderengine::LayerSettings>&,
+                          const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                          base::unique_fd&&) -> void {
+                resultPromise->set_value({NO_ERROR, base::unique_fd()});
+            });
 
-    status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
-                                              std::move(bufferFence), &drawFence);
-    ASSERT_EQ(NO_ERROR, result);
+    std::future<renderengine::RenderEngineResult> result =
+            mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+    ASSERT_TRUE(result.valid());
+    auto [status, _] = result.get();
+    ASSERT_EQ(NO_ERROR, status);
 }
 
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 8e666d5..a7176d3 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -178,6 +178,10 @@
 
 void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
     ATRACE_CALL();
+    // This is a no-op in SkiaRenderEngine.
+    if (getRenderEngineType() != RenderEngineType::THREADED) {
+        return;
+    }
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
@@ -194,6 +198,10 @@
 
 void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
     ATRACE_CALL();
+    // This is a no-op in SkiaRenderEngine.
+    if (getRenderEngineType() != RenderEngineType::THREADED) {
+        return;
+    }
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
@@ -292,7 +300,7 @@
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+            ATRACE_NAME("REThreaded::cleanupPostRender");
             instance.cleanupPostRender();
         });
     }
@@ -304,27 +312,34 @@
     return mRenderEngine->canSkipPostRenderCleanup();
 }
 
-status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
-                                          const std::vector<const LayerSettings*>& layers,
-                                          const std::shared_ptr<ExternalTexture>& buffer,
-                                          const bool useFramebufferCache,
-                                          base::unique_fd&& bufferFence,
-                                          base::unique_fd* drawFence) {
+void RenderEngineThreaded::drawLayersInternal(
+        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
+    resultPromise->set_value({NO_ERROR, base::unique_fd()});
+    return;
+}
+
+std::future<RenderEngineResult> RenderEngineThreaded::drawLayers(
+        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
     ATRACE_CALL();
-    std::promise<status_t> resultPromise;
-    std::future<status_t> resultFuture = resultPromise.get_future();
+    const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
+    std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+    int fd = bufferFence.release();
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
-                             &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+        mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache,
+                             fd](renderengine::RenderEngine& instance) {
             ATRACE_NAME("REThreaded::drawLayers");
-            status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
-                                                  std::move(bufferFence), drawFence);
-            resultPromise.set_value(status);
+            instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
+                                        useFramebufferCache, base::unique_fd(fd));
         });
     }
     mCondition.notify_one();
-    return resultFuture.get();
+    return resultFuture;
 }
 
 void RenderEngineThreaded::cleanFramebufferCache() {
@@ -374,6 +389,20 @@
     mCondition.notify_one();
 }
 
+std::optional<pid_t> RenderEngineThreaded::getRenderEngineTid() const {
+    std::promise<pid_t> tidPromise;
+    std::future<pid_t> tidFuture = tidPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&tidPromise](renderengine::RenderEngine& instance) {
+            tidPromise.set_value(gettid());
+        });
+    }
+
+    mCondition.notify_one();
+    return std::make_optional(tidFuture.get());
+}
+
 } // namespace threaded
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index b197df7..1ba72dd 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -56,21 +56,27 @@
     void useProtectedContext(bool useProtectedContext) override;
     void cleanupPostRender() override;
 
-    status_t drawLayers(const DisplaySettings& display,
-                        const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
+    std::future<RenderEngineResult> drawLayers(const DisplaySettings& display,
+                                               const std::vector<LayerSettings>& layers,
+                                               const std::shared_ptr<ExternalTexture>& buffer,
+                                               const bool useFramebufferCache,
+                                               base::unique_fd&& bufferFence) override;
 
     void cleanFramebufferCache() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override;
     void onActiveDisplaySizeChanged(ui::Size size) override;
+    std::optional<pid_t> getRenderEngineTid() const override;
 
 protected:
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
     bool canSkipPostRenderCleanup() const override;
+    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+                            const DisplaySettings& display,
+                            const std::vector<LayerSettings>& layers,
+                            const std::shared_ptr<ExternalTexture>& buffer,
+                            const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
new file mode 100644
index 0000000..390b228
--- /dev/null
+++ b/libs/shaders/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+    name: "libshaders",
+
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "android.hardware.graphics.common-V3-ndk",
+        "android.hardware.graphics.common@1.2",
+    ],
+
+    static_libs: [
+        "libmath",
+        "libtonemap",
+        "libui-types",
+    ],
+
+    srcs: [
+        "shaders.cpp",
+    ],
+}
diff --git a/libs/shaders/OWNERS b/libs/shaders/OWNERS
new file mode 100644
index 0000000..6d91da3
--- /dev/null
+++ b/libs/shaders/OWNERS
@@ -0,0 +1,4 @@
+alecmouri@google.com
+jreck@google.com
+sallyqi@google.com
+scroggo@google.com
\ No newline at end of file
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
new file mode 100644
index 0000000..712a27a
--- /dev/null
+++ b/libs/shaders/include/shaders/shaders.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+#include <tonemap/tonemap.h>
+#include <ui/GraphicTypes.h>
+#include <cstddef>
+
+namespace android::shaders {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+    // Input dataspace of the source colors.
+    const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+    // Working dataspace for the output surface, for conversion from linear space.
+    const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+    // Sets whether alpha premultiplication must be undone.
+    // This is required if the source colors use premultiplied alpha and is not opaque.
+    const bool undoPremultipliedAlpha = false;
+
+    // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear
+    // RGB. This is used when Skia is expected to color manage the input image based on the
+    // dataspace of the provided source image and destination surface. SkRuntimeEffects use the
+    // destination color space as the working color space. RenderEngine deliberately sets the color
+    // space for input images and destination surfaces to be the same whenever LinearEffects are
+    // expected to be used so that color-management is controlled by RenderEngine, but other users
+    // of a LinearEffect may not be able to control the color space of the images and surfaces. So
+    // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
+    // dataspace for correct conversion to linear colors.
+    ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+};
+
+static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
+    return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
+            lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha &&
+            lhs.fakeInputDataspace == rhs.fakeInputDataspace;
+}
+
+struct LinearEffectHasher {
+    // Inspired by art/runtime/class_linker.cc
+    // Also this is what boost:hash_combine does
+    static size_t HashCombine(size_t seed, size_t val) {
+        return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+    }
+    size_t operator()(const LinearEffect& le) const {
+        size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
+        result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
+        result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
+        return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace));
+    }
+};
+
+// Generates a shader string that applies color transforms in linear space.
+// Typical use-cases supported:
+// 1. Apply tone-mapping
+// 2. Apply color transform matrices in linear space
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+                                                              const mat4& colorTransform,
+                                                              float maxDisplayLuminance,
+                                                              float maxLuminance);
+
+} // namespace android::shaders
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
new file mode 100644
index 0000000..6019c4a
--- /dev/null
+++ b/libs/shaders/shaders.cpp
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shaders/shaders.h>
+
+#include <tonemap/tonemap.h>
+
+#include <optional>
+
+#include <math/mat4.h>
+#include <system/graphics-base-v1.0.h>
+#include <ui/ColorSpace.h>
+
+namespace android::shaders {
+
+static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(
+        ui::Dataspace dataspace) {
+    return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
+}
+
+static void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+
+                float3 EOTF(float3 color) {
+                    float m1 = (2610.0 / 4096.0) / 4.0;
+                    float m2 = (2523.0 / 4096.0) * 128.0;
+                    float c1 = (3424.0 / 4096.0);
+                    float c2 = (2413.0 / 4096.0) * 32.0;
+                    float c3 = (2392.0 / 4096.0) * 32.0;
+
+                    float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+                    tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+                    return pow(tmp, 1.0 / float3(m1));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                float EOTF_channel(float channel) {
+                    const float a = 0.17883277;
+                    const float b = 0.28466892;
+                    const float c = 0.55991073;
+                    return channel <= 0.5 ? channel * channel / 3.0 :
+                            (exp((channel - c) / a) + b) / 12.0;
+                }
+
+                float3 EOTF(float3 color) {
+                    return float3(EOTF_channel(color.r), EOTF_channel(color.g),
+                            EOTF_channel(color.b));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            shader.append(R"(
+                float3 EOTF(float3 color) {
+                    return color;
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+            shader.append(R"(
+
+                float EOTF_sRGB(float srgb) {
+                    return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 0.45);
+                }
+
+                float3 EOTF_sRGB(float3 srgb) {
+                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                }
+
+                float3 EOTF(float3 srgb) {
+                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+            shader.append(R"(
+
+                float EOTF_sRGB(float srgb) {
+                    return pow(srgb, 2.2);
+                }
+
+                float3 EOTF_sRGB(float3 srgb) {
+                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                }
+
+                float3 EOTF(float3 srgb) {
+                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+            shader.append(R"(
+
+                float EOTF_sRGB(float srgb) {
+                    return pow(srgb, 2.6);
+                }
+
+                float3 EOTF_sRGB(float3 srgb) {
+                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                }
+
+                float3 EOTF(float3 srgb) {
+                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+            shader.append(R"(
+
+                float EOTF_sRGB(float srgb) {
+                    return pow(srgb, 2.8);
+                }
+
+                float3 EOTF_sRGB(float3 srgb) {
+                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                }
+
+                float3 EOTF(float3 srgb) {
+                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_SRGB:
+        default:
+            shader.append(R"(
+
+                float EOTF_sRGB(float srgb) {
+                    return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+                }
+
+                float3 EOTF_sRGB(float3 srgb) {
+                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                }
+
+                float3 EOTF(float3 srgb) {
+                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                }
+            )");
+            break;
+    }
+}
+
+static void generateXYZTransforms(std::string& shader) {
+    shader.append(R"(
+        uniform float4x4 in_rgbToXyz;
+        uniform float4x4 in_xyzToRgb;
+        float3 ToXYZ(float3 rgb) {
+            return (in_rgbToXyz * float4(rgb, 1.0)).rgb;
+        }
+
+        float3 ToRGB(float3 xyz) {
+            return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+        }
+    )");
+}
+
+// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
+static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,
+                                           ui::Dataspace outputDataspace, std::string& shader) {
+    switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * 10000.0;
+                    }
+                )");
+            break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * 1000.0 * pow(xyz.y, 0.2);
+                    }
+                )");
+            break;
+        default:
+            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+                case HAL_DATASPACE_TRANSFER_ST2084:
+                case HAL_DATASPACE_TRANSFER_HLG:
+                    // SDR -> HDR tonemap
+                    shader.append(R"(
+                            float3 ScaleLuminance(float3 xyz) {
+                                return xyz * in_libtonemap_inputMaxLuminance;
+                            }
+                        )");
+                    break;
+                default:
+                    // Input and output are both SDR, so no tone-mapping is expected so
+                    // no-op the luminance normalization.
+                    shader.append(R"(
+                                float3 ScaleLuminance(float3 xyz) {
+                                    return xyz * in_libtonemap_displayMaxLuminance;
+                                }
+                            )");
+                    break;
+            }
+    }
+}
+
+// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
+                                                  std::string& shader) {
+    switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / 10000.0;
+                    }
+                )");
+            break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
+                    }
+                )");
+            break;
+        default:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / in_libtonemap_displayMaxLuminance;
+                    }
+                )");
+            break;
+    }
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+                         std::string& shader) {
+    shader.append(tonemap::getToneMapper()
+                          ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
+                                                          toAidlDataspace(outputDataspace))
+                          .c_str());
+
+    generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
+    generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+
+    shader.append(R"(
+            float3 OOTF(float3 linearRGB, float3 xyz) {
+                float3 scaledLinearRGB = ScaleLuminance(linearRGB);
+                float3 scaledXYZ = ScaleLuminance(xyz);
+
+                float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);
+
+                return NormalizeLuminance(scaledXYZ * gain);
+            }
+        )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, std::string& shader) {
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+
+                float3 OETF(float3 xyz) {
+                    float m1 = (2610.0 / 4096.0) / 4.0;
+                    float m2 = (2523.0 / 4096.0) * 128.0;
+                    float c1 = (3424.0 / 4096.0);
+                    float c2 = (2413.0 / 4096.0) * 32.0;
+                    float c3 = (2392.0 / 4096.0) * 32.0;
+
+                    float3 tmp = pow(xyz, float3(m1));
+                    tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+                    return pow(tmp, float3(m2));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                float OETF_channel(float channel) {
+                    const float a = 0.17883277;
+                    const float b = 0.28466892;
+                    const float c = 0.55991073;
+                    return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+                            a * log(12.0 * channel - b) + c;
+                }
+
+                float3 OETF(float3 linear) {
+                    return float3(OETF_channel(linear.r), OETF_channel(linear.g),
+                            OETF_channel(linear.b));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            shader.append(R"(
+                float3 OETF(float3 linear) {
+                    return linear;
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+            shader.append(R"(
+                float OETF_sRGB(float linear) {
+                    return linear <= 0.018 ?
+                            linear * 4.50 : (pow(linear, 0.45) * 1.099) - 0.099;
+                }
+
+                float3 OETF_sRGB(float3 linear) {
+                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                }
+
+                float3 OETF(float3 linear) {
+                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+            shader.append(R"(
+                float OETF_sRGB(float linear) {
+                    return pow(linear, (1.0 / 2.2));
+                }
+
+                float3 OETF_sRGB(float3 linear) {
+                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                }
+
+                float3 OETF(float3 linear) {
+                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+            shader.append(R"(
+                float OETF_sRGB(float linear) {
+                    return pow(linear, (1.0 / 2.6));
+                }
+
+                float3 OETF_sRGB(float3 linear) {
+                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                }
+
+                float3 OETF(float3 linear) {
+                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+            shader.append(R"(
+                float OETF_sRGB(float linear) {
+                    return pow(linear, (1.0 / 2.8));
+                }
+
+                float3 OETF_sRGB(float3 linear) {
+                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                }
+
+                float3 OETF(float3 linear) {
+                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_SRGB:
+        default:
+            shader.append(R"(
+                float OETF_sRGB(float linear) {
+                    return linear <= 0.0031308 ?
+                            linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+                }
+
+                float3 OETF_sRGB(float3 linear) {
+                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                }
+
+                float3 OETF(float3 linear) {
+                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                }
+            )");
+            break;
+    }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
+    shader.append(R"(
+        uniform shader child;
+        half4 main(float2 xy) {
+            float4 c = float4(child.eval(xy));
+    )");
+    if (undoPremultipliedAlpha) {
+        shader.append(R"(
+            c.rgb = c.rgb / (c.a + 0.0019);
+        )");
+    }
+    shader.append(R"(
+        float3 linearRGB = EOTF(c.rgb);
+        float3 xyz = ToXYZ(linearRGB);
+        c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
+    )");
+    if (undoPremultipliedAlpha) {
+        shader.append(R"(
+            c.rgb = c.rgb * (c.a + 0.0019);
+        )");
+    }
+    shader.append(R"(
+            return c;
+        }
+    )");
+}
+
+// please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+        case HAL_DATASPACE_STANDARD_BT709:
+            return ColorSpace::sRGB();
+        case HAL_DATASPACE_STANDARD_DCI_P3:
+            return ColorSpace::DisplayP3();
+        case HAL_DATASPACE_STANDARD_BT2020:
+        case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+            return ColorSpace::BT2020();
+        case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+            return ColorSpace::AdobeRGB();
+        // TODO(b/208290320): BT601 format and variants return different primaries
+        case HAL_DATASPACE_STANDARD_BT601_625:
+        case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+        case HAL_DATASPACE_STANDARD_BT601_525:
+        case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+        // TODO(b/208290329): BT407M format returns different primaries
+        case HAL_DATASPACE_STANDARD_BT470M:
+        // TODO(b/208290904): FILM format returns different primaries
+        case HAL_DATASPACE_STANDARD_FILM:
+        case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+        default:
+            return ColorSpace::sRGB();
+    }
+}
+
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
+    std::string shaderString;
+    generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
+                         ? linearEffect.inputDataspace
+                         : linearEffect.fakeInputDataspace,
+                 shaderString);
+    generateXYZTransforms(shaderString);
+    generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+    generateOETF(linearEffect.outputDataspace, shaderString);
+    generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+    return shaderString;
+}
+
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+    std::vector<uint8_t> result;
+    result.resize(sizeof(value));
+    std::memcpy(result.data(), &value, sizeof(value));
+    return result;
+}
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+                                                              const mat4& colorTransform,
+                                                              float maxDisplayLuminance,
+                                                              float maxLuminance) {
+    std::vector<tonemap::ShaderUniform> uniforms;
+    if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
+        uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())});
+        uniforms.push_back(
+                {.name = "in_xyzToRgb", .value = buildUniformValue<mat4>(colorTransform)});
+    } else {
+        ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+        ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+        uniforms.push_back({.name = "in_rgbToXyz",
+                            .value = buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ()))});
+        uniforms.push_back({.name = "in_xyzToRgb",
+                            .value = buildUniformValue<mat4>(
+                                    colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
+    }
+
+    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};
+
+    for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
+        uniforms.push_back(uniform);
+    }
+
+    return uniforms;
+}
+
+} // namespace android::shaders
\ No newline at end of file
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
new file mode 100644
index 0000000..5360fe2
--- /dev/null
+++ b/libs/tonemap/Android.bp
@@ -0,0 +1,43 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+    name: "libtonemap",
+    vendor_available: true,
+
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "android.hardware.graphics.common-V3-ndk",
+        "liblog",
+    ],
+
+    static_libs: [
+        "libmath",
+    ],
+
+    srcs: [
+        "tonemap.cpp",
+    ],
+}
diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS
new file mode 100644
index 0000000..6d91da3
--- /dev/null
+++ b/libs/tonemap/OWNERS
@@ -0,0 +1,4 @@
+alecmouri@google.com
+jreck@google.com
+sallyqi@google.com
+scroggo@google.com
\ No newline at end of file
diff --git a/libs/tonemap/TEST_MAPPING b/libs/tonemap/TEST_MAPPING
new file mode 100644
index 0000000..00f83ba
--- /dev/null
+++ b/libs/tonemap/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "librenderengine_test"
+    },
+    {
+      "name": "libtonemap_test"
+    }
+  ]
+}
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
new file mode 100644
index 0000000..bd7b72d
--- /dev/null
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <math/vec3.h>
+
+#include <string>
+#include <vector>
+
+namespace android::tonemap {
+
+// Describes a shader uniform
+// The shader uniform is intended to be passed into a SkRuntimeShaderBuilder, i.e.:
+//
+// SkRuntimeShaderBuilder builder;
+// builder.uniform(<uniform name>).set(<uniform value>.data(), <uniform value>.size());
+struct ShaderUniform {
+    // The name of the uniform, used for binding into a shader.
+    // The shader must contain a uniform whose name matches this.
+    std::string name;
+
+    // The value for the uniform, which should be bound to the uniform identified by <name>
+    std::vector<uint8_t> value;
+};
+
+// Describes metadata which may be used for constructing the shader uniforms.
+// This metadata should not be used for manipulating the source code of the shader program directly,
+// as otherwise caching by other system of these shaders may break.
+struct Metadata {
+    float displayMaxLuminance = 0.0;
+    float contentMaxLuminance = 0.0;
+};
+
+class ToneMapper {
+public:
+    virtual ~ToneMapper() {}
+    // Constructs a tonemap shader whose shader language is SkSL, which tonemaps from an
+    // input whose dataspace is described by sourceDataspace, to an output whose dataspace
+    // is described by destinationDataspace
+    //
+    // The returned shader string *must* contain a function with the following signature:
+    // float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz);
+    //
+    // The arguments are:
+    // * linearRGB is the absolute nits of the RGB pixels in linear space
+    // * xyz is linearRGB converted into XYZ
+    //
+    // libtonemap_LookupTonemapGain() returns a float representing the amount by which to scale the
+    // absolute nits of the pixels. This function may be plugged into any existing SkSL shader, and
+    // is expected to look something like this:
+    //
+    // vec3 rgb = ...;
+    // // apply the EOTF based on the incoming dataspace to convert to linear nits.
+    // vec3 linearRGB = applyEOTF(rgb);
+    // // apply a RGB->XYZ matrix float3
+    // vec3 xyz = toXYZ(linearRGB);
+    // // Scale the luminance based on the content standard
+    // vec3 absoluteRGB = ScaleLuminance(linearRGB);
+    // vec3 absoluteXYZ = ScaleLuminance(xyz);
+    // float gain = libtonemap_LookupTonemapGain(absoluteRGB, absoluteXYZ);
+    // // Normalize the luminance back down to a [0, 1] range
+    // xyz = NormalizeLuminance(absoluteXYZ * gain);
+    // // apply a XYZ->RGB matrix and apply the output OETf.
+    // vec3 finalColor = applyOETF(ToRGB(xyz));
+    // ...
+    //
+    // Helper methods in this shader should be prefixed with "libtonemap_". Accordingly, libraries
+    // which consume this shader must *not* contain any methods prefixed with "libtonemap_" to
+    // guarantee that there are no conflicts in name resolution.
+    virtual std::string generateTonemapGainShaderSkSL(
+            aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+            aidl::android::hardware::graphics::common::Dataspace destinationDataspace) = 0;
+
+    // Constructs uniform descriptions that correspond to those that are generated for the tonemap
+    // shader. Uniforms must be prefixed with "in_libtonemap_". Libraries which consume this shader
+    // must not bind any new uniforms that begin with this prefix.
+    //
+    // Downstream shaders may assume the existence of the uniform in_libtonemap_displayMaxLuminance
+    // and in_libtonemap_inputMaxLuminance, in order to assist with scaling and normalizing
+    // luminance as described in the documentation for generateTonemapGainShaderSkSL(). That is,
+    // shaders plugging in a tone-mapping shader returned by generateTonemapGainShaderSkSL() may
+    // assume that there are predefined floats in_libtonemap_displayMaxLuminance and
+    // in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader.
+    virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0;
+
+    // CPU implementation of the tonemapping gain. This must match the GPU implementation returned
+    // by generateTonemapGainShaderSKSL() above, with some epsilon difference to account for
+    // differences in hardware precision.
+    //
+    // The gain is computed assuming an input described by sourceDataspace, tonemapped to an output
+    // 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(
+            aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+            aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+            vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0;
+};
+
+// Retrieves a tonemapper instance.
+// This instance is globally constructed.
+ToneMapper* getToneMapper();
+
+} // namespace android::tonemap
\ No newline at end of file
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
new file mode 100644
index 0000000..f46f3fa
--- /dev/null
+++ b/libs/tonemap/tests/Android.bp
@@ -0,0 +1,39 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+    name: "libtonemap_test",
+    test_suites: ["device-tests"],
+    srcs: [
+        "tonemap_test.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.graphics.common-V3-ndk",
+    ],
+    static_libs: [
+        "libmath",
+        "libgmock",
+        "libgtest",
+        "libtonemap",
+    ],
+}
diff --git a/libs/tonemap/tests/tonemap_test.cpp b/libs/tonemap/tests/tonemap_test.cpp
new file mode 100644
index 0000000..7a7958f
--- /dev/null
+++ b/libs/tonemap/tests/tonemap_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <tonemap/tonemap.h>
+#include <cmath>
+
+namespace android {
+
+using testing::HasSubstr;
+
+struct TonemapTest : public ::testing::Test {};
+
+TEST_F(TonemapTest, generateShaderSkSLUniforms_containsDefaultUniforms) {
+    static const constexpr float kDisplayMaxLuminance = 1.f;
+    static const constexpr float kContentMaxLuminance = 2.f;
+    tonemap::Metadata metadata{.displayMaxLuminance = kDisplayMaxLuminance,
+                               .contentMaxLuminance = kContentMaxLuminance};
+    const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata);
+
+    ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+                  return data.name == "in_libtonemap_displayMaxLuminance";
+              }));
+    ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+                  return data.name == "in_libtonemap_inputMaxLuminance";
+              }));
+
+    // Smoke check that metadata values are "real", specifically that they're non-zero and actually
+    // numbers. This is to help avoid shaders using these uniforms from dividing by zero or other
+    // catastrophic errors.
+    const auto& displayLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+                                 return data.name == "in_libtonemap_displayMaxLuminance";
+                             })->value;
+
+    float displayLumFloat = 0.f;
+    std::memcpy(&displayLumFloat, displayLum.data(), displayLum.size());
+    EXPECT_FALSE(std::isnan(displayLumFloat));
+    EXPECT_GT(displayLumFloat, 0);
+
+    const auto& contentLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+                                 return data.name == "in_libtonemap_inputMaxLuminance";
+                             })->value;
+
+    float contentLumFloat = 0.f;
+    std::memcpy(&contentLumFloat, contentLum.data(), contentLum.size());
+    EXPECT_FALSE(std::isnan(contentLumFloat));
+    EXPECT_GT(contentLumFloat, 0);
+}
+
+TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) {
+    const auto shader =
+            tonemap::getToneMapper()
+                    ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common::
+                                                            Dataspace::BT2020_ITU_PQ,
+                                                    aidl::android::hardware::graphics::common::
+                                                            Dataspace::DISPLAY_P3);
+
+    // Other tests such as librenderengine_test will plug in the shader to check compilation.
+    EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)"));
+}
+
+} // namespace android
diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp
new file mode 100644
index 0000000..c2372fe
--- /dev/null
+++ b/libs/tonemap/tonemap.cpp
@@ -0,0 +1,666 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <tonemap/tonemap.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <mutex>
+#include <type_traits>
+
+namespace android::tonemap {
+
+namespace {
+
+// Flag containing the variant of tone map algorithm to use.
+enum class ToneMapAlgorithm {
+    AndroidO,  // Default algorithm in place since Android O,
+    Android13, // Algorithm used in Android 13.
+};
+
+static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::Android13;
+
+static const constexpr auto kTransferMask =
+        static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_MASK);
+static const constexpr auto kTransferST2084 =
+        static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_ST2084);
+static const constexpr auto kTransferHLG =
+        static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_HLG);
+
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+    std::vector<uint8_t> result;
+    result.resize(sizeof(value));
+    std::memcpy(result.data(), &value, sizeof(value));
+    return result;
+}
+
+class ToneMapperO : public ToneMapper {
+public:
+    std::string generateTonemapGainShaderSkSL(
+            aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+            aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+        const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+        const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+        std::string program;
+        // Define required uniforms
+        program.append(R"(
+                uniform float in_libtonemap_displayMaxLuminance;
+                uniform float in_libtonemap_inputMaxLuminance;
+            )");
+        switch (sourceDataspaceInt & kTransferMask) {
+            case kTransferST2084:
+            case kTransferHLG:
+                switch (destinationDataspaceInt & kTransferMask) {
+                    case kTransferST2084:
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(vec3 xyz) {
+                                        return 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.
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(vec3 xyz) {
+                                        return clamp(xyz.y, 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.
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(vec3 xyz) {
+                                        float maxInLumi = in_libtonemap_inputMaxLuminance;
+                                        float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+                                        float nits = 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 (maxInLumi <= maxOutLumi) {
+                                            return xyz.y;
+                                        } else {
+
+                                            // three control points
+                                            const float x0 = 10.0;
+                                            const float y0 = 17.0;
+                                            float x1 = maxOutLumi * 0.75;
+                                            float y1 = x1;
+                                            float x2 = x1 + (maxInLumi - x1) / 2.0;
+                                            float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+                                            // horizontal distances between the last three
+                                            // control points
+                                            float h12 = x2 - x1;
+                                            float h23 = maxInLumi - x2;
+                                            // tangents at the last three control points
+                                            float m1 = (y2 - y1) / h12;
+                                            float m3 = (maxOutLumi - y2) / h23;
+                                            float m2 = (m1 + m3) / 2.0;
+
+                                            if (nits < x0) {
+                                                // scale [0.0, x0] to [0.0, y0] linearly
+                                                float slope = y0 / x0;
+                                                return nits * slope;
+                                            } else if (nits < x1) {
+                                                // scale [x0, x1] to [y0, y1] linearly
+                                                float slope = (y1 - y0) / (x1 - x0);
+                                                nits = y0 + (nits - x0) * slope;
+                                            } else if (nits < x2) {
+                                                // scale [x1, x2] to [y1, y2] using Hermite interp
+                                                float t = (nits - x1) / h12;
+                                                nits = (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
+                                                float t = (nits - x2) / h23;
+                                                nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) *
+                                                        (1.0 - t) * (1.0 - t) + (maxOutLumi *
+                                                        (3.0 - 2.0 * t) + h23 * m3 *
+                                                        (t - 1.0)) * t * t;
+                                            }
+                                        }
+
+                                        return nits;
+                                    }
+                                )");
+                        break;
+                }
+                break;
+            default:
+                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.
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(vec3 xyz) {
+                                        const float maxOutLumi = 3000.0;
+
+                                        const float x0 = 5.0;
+                                        const float y0 = 2.5;
+                                        float x1 = in_libtonemap_displayMaxLuminance * 0.7;
+                                        float y1 = maxOutLumi * 0.15;
+                                        float x2 = in_libtonemap_displayMaxLuminance * 0.9;
+                                        float y2 = maxOutLumi * 0.45;
+                                        float x3 = in_libtonemap_displayMaxLuminance;
+                                        float y3 = maxOutLumi;
+
+                                        float c1 = y1 / 3.0;
+                                        float c2 = y2 / 2.0;
+                                        float c3 = y3 / 1.5;
+
+                                        float nits = xyz.y;
+
+                                        if (nits <= x0) {
+                                            // scale [0.0, x0] to [0.0, y0] linearly
+                                            float slope = y0 / x0;
+                                            return nits * slope;
+                                        } else if (nits <= x1) {
+                                            // scale [x0, x1] to [y0, y1] using a curve
+                                            float t = (nits - x0) / (x1 - x0);
+                                            nits = (1.0 - t) * (1.0 - t) * y0 +
+                                                    2.0 * (1.0 - t) * t * c1 + t * t * y1;
+                                        } else if (nits <= x2) {
+                                            // scale [x1, x2] to [y1, y2] using a curve
+                                            float t = (nits - x1) / (x2 - x1);
+                                            nits = (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
+                                            float t = (nits - x2) / (x3 - x2);
+                                            nits = (1.0 - t) * (1.0 - t) * y2 +
+                                                    2.0 * (1.0 - t) * t * c3 + t * t * y3;
+                                        }
+
+                                        return nits;
+                                    }
+                                )");
+                        break;
+                    default:
+                        // For completeness, this is tone-mapping from SDR to SDR, where this is
+                        // just a no-op.
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(vec3 xyz) {
+                                        return xyz.y;
+                                    }
+                                )");
+                        break;
+                }
+                break;
+        }
+
+        program.append(R"(
+            float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+                if (xyz.y <= 0.0) {
+                    return 1.0;
+                }
+                return libtonemap_ToneMapTargetNits(xyz) / xyz.y;
+            }
+        )");
+        return program;
+    }
+
+    std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+        std::vector<ShaderUniform> uniforms;
+
+        uniforms.reserve(2);
+
+        uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+                            .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+        uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+                            .value = buildUniformValue<float>(metadata.contentMaxLuminance)});
+        return uniforms;
+    }
+
+    double 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);
+
+        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.
+
+                        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;
+
+                            // 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] 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;
+                }
+        }
+
+        return targetNits / xyz.y;
+    }
+};
+
+class ToneMapper13 : public ToneMapper {
+private:
+    double OETF_ST2084(double nits) {
+        nits = nits / 10000.0;
+        double m1 = (2610.0 / 4096.0) / 4.0;
+        double m2 = (2523.0 / 4096.0) * 128.0;
+        double c1 = (3424.0 / 4096.0);
+        double c2 = (2413.0 / 4096.0) * 32.0;
+        double c3 = (2392.0 / 4096.0) * 32.0;
+
+        double tmp = std::pow(nits, m1);
+        tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+        return std::pow(tmp, m2);
+    }
+
+    double OETF_HLG(double nits) {
+        nits = nits / 1000.0;
+        const double a = 0.17883277;
+        const double b = 0.28466892;
+        const double c = 0.55991073;
+        return nits <= 1.0 / 12.0 ? std::sqrt(3.0 * nits) : a * std::log(12.0 * nits - b) + c;
+    }
+
+public:
+    std::string generateTonemapGainShaderSkSL(
+            aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+            aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+        const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+        const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+        std::string program;
+        // Input uniforms
+        program.append(R"(
+                uniform float in_libtonemap_displayMaxLuminance;
+                uniform float in_libtonemap_inputMaxLuminance;
+            )");
+        switch (sourceDataspaceInt & kTransferMask) {
+            case kTransferST2084:
+            case kTransferHLG:
+                switch (destinationDataspaceInt & kTransferMask) {
+                    case kTransferST2084:
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(float maxRGB) {
+                                        return 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.
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(float maxRGB) {
+                                        return clamp(maxRGB, 0.0, 1000.0);
+                                    }
+                                )");
+                        break;
+
+                    default:
+                        switch (sourceDataspaceInt & kTransferMask) {
+                            case kTransferST2084:
+                                program.append(R"(
+                                        float libtonemap_OETFTone(float channel) {
+                                            channel = channel / 10000.0;
+                                            float m1 = (2610.0 / 4096.0) / 4.0;
+                                            float m2 = (2523.0 / 4096.0) * 128.0;
+                                            float c1 = (3424.0 / 4096.0);
+                                            float c2 = (2413.0 / 4096.0) * 32.0;
+                                            float c3 = (2392.0 / 4096.0) * 32.0;
+
+                                            float tmp = pow(channel, float(m1));
+                                            tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+                                            return pow(tmp, float(m2));
+                                        }
+                                    )");
+                                break;
+                            case kTransferHLG:
+                                program.append(R"(
+                                        float libtonemap_OETFTone(float channel) {
+                                            channel = channel / 1000.0;
+                                            const float a = 0.17883277;
+                                            const float b = 0.28466892;
+                                            const float c = 0.55991073;
+                                            return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+                                                    a * log(12.0 * channel - b) + c;
+                                        }
+                                    )");
+                                break;
+                        }
+                        // 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_ToneMapTargetNits(float maxRGB) {
+                                    float maxInLumi = in_libtonemap_inputMaxLuminance;
+                                    float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+                                    float nits = maxRGB;
+
+                                    float x1 = maxOutLumi * 0.65;
+                                    float y1 = x1;
+
+                                    float x3 = maxInLumi;
+                                    float y3 = maxOutLumi;
+
+                                    float x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+                                    float y2 = maxOutLumi * 0.9;
+
+                                    float greyNorm1 = libtonemap_OETFTone(x1);
+                                    float greyNorm2 = libtonemap_OETFTone(x2);
+                                    float greyNorm3 = libtonemap_OETFTone(x3);
+
+                                    float slope1 = 0;
+                                    float slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+                                    float slope3 = (y3 - y2 ) / (greyNorm3 - greyNorm2);
+
+                                    if (nits < x1) {
+                                        return nits;
+                                    }
+
+                                    if (nits > maxInLumi) {
+                                        return maxOutLumi;
+                                    }
+
+                                    float greyNits = libtonemap_OETFTone(nits);
+
+                                    if (greyNits <= greyNorm2) {
+                                        nits = (greyNits - greyNorm2) * slope2 + y2;
+                                    } else if (greyNits <= greyNorm3) {
+                                        nits = (greyNits - greyNorm3) * slope3 + y3;
+                                    } else {
+                                        nits = maxOutLumi;
+                                    }
+
+                                    return nits;
+                                }
+                                )");
+                        break;
+                }
+                break;
+            default:
+                // Inverse tone-mapping and SDR-SDR mapping is not supported.
+                program.append(R"(
+                            float libtonemap_ToneMapTargetNits(float maxRGB) {
+                                return maxRGB;
+                            }
+                        )");
+                break;
+        }
+
+        program.append(R"(
+            float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+                float maxRGB = max(linearRGB.r, max(linearRGB.g, linearRGB.b));
+                if (maxRGB <= 0.0) {
+                    return 1.0;
+                }
+                return libtonemap_ToneMapTargetNits(maxRGB) / maxRGB;
+            }
+        )");
+        return program;
+    }
+
+    std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+        // Hardcode the max content luminance to a "reasonable" level
+        static const constexpr float kContentMaxLuminance = 4000.f;
+        std::vector<ShaderUniform> uniforms;
+        uniforms.reserve(2);
+        uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+                            .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+        uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+                            .value = buildUniformValue<float>(kContentMaxLuminance)});
+        return uniforms;
+    }
+
+    double 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});
+
+        if (maxRGB <= 0.0) {
+            return 1.0;
+        }
+
+        const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+        const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+        double targetNits = 0.0;
+        switch (sourceDataspaceInt & kTransferMask) {
+            case kTransferST2084:
+            case kTransferHLG:
+                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.
+
+                        double maxInLumi = 4000;
+                        double maxOutLumi = metadata.displayMaxLuminance;
+
+                        targetNits = maxRGB;
+
+                        double x1 = maxOutLumi * 0.65;
+                        double y1 = x1;
+
+                        double x3 = maxInLumi;
+                        double y3 = maxOutLumi;
+
+                        double x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+                        double y2 = maxOutLumi * 0.9;
+
+                        double greyNorm1 = 0.0;
+                        double greyNorm2 = 0.0;
+                        double greyNorm3 = 0.0;
+
+                        if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+                            greyNorm1 = OETF_ST2084(x1);
+                            greyNorm2 = OETF_ST2084(x2);
+                            greyNorm3 = OETF_ST2084(x3);
+                        } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+                            greyNorm1 = OETF_HLG(x1);
+                            greyNorm2 = OETF_HLG(x2);
+                            greyNorm3 = OETF_HLG(x3);
+                        }
+
+                        double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+                        double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
+
+                        if (targetNits < x1) {
+                            break;
+                        }
+
+                        if (targetNits > maxInLumi) {
+                            targetNits = maxOutLumi;
+                            break;
+                        }
+
+                        double greyNits = 0.0;
+                        if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+                            greyNits = OETF_ST2084(targetNits);
+                        } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+                            greyNits = OETF_HLG(targetNits);
+                        }
+
+                        if (greyNits <= greyNorm2) {
+                            targetNits = (greyNits - greyNorm2) * slope2 + y2;
+                        } else if (greyNits <= greyNorm3) {
+                            targetNits = (greyNits - greyNorm3) * slope3 + y3;
+                        } else {
+                            targetNits = maxOutLumi;
+                        }
+                        break;
+                }
+                break;
+            default:
+                switch (destinationDataspaceInt & kTransferMask) {
+                    case kTransferST2084:
+                    case kTransferHLG:
+                    default:
+                        targetNits = maxRGB;
+                        break;
+                }
+                break;
+        }
+
+        return targetNits / maxRGB;
+    }
+};
+
+} // namespace
+
+ToneMapper* getToneMapper() {
+    static std::once_flag sOnce;
+    static std::unique_ptr<ToneMapper> sToneMapper;
+
+    std::call_once(sOnce, [&] {
+        switch (kToneMapAlgorithm) {
+            case ToneMapAlgorithm::AndroidO:
+                sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapperO());
+                break;
+            case ToneMapAlgorithm::Android13:
+                sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapper13());
+        }
+    });
+
+    return sToneMapper.get();
+}
+} // namespace android::tonemap
\ No newline at end of file
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 506e308..006c478 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -86,6 +86,7 @@
 
     export_include_dirs: [
         "include",
+        "include_mock",
         "include_private",
         "include_types",
     ],
@@ -137,7 +138,7 @@
         "HdrCapabilities.cpp",
         "PixelFormat.cpp",
         "PublicFormat.cpp",
-        "Size.cpp",
+        "StaticAsserts.cpp",
         "StaticDisplayInfo.cpp",
     ],
 
@@ -159,7 +160,7 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
-        "android.hardware.graphics.common-V2-ndk",
+        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
@@ -176,7 +177,7 @@
 
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
-        "android.hardware.graphics.common-V2-ndk",
+        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libgralloctypes",
     ],
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 1f006ce..073da89 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -302,6 +302,8 @@
             return std::string("RGB_565");
         case android::PIXEL_FORMAT_BGRA_8888:
             return std::string("BGRA_8888");
+        case android::PIXEL_FORMAT_R_8:
+            return std::string("R_8");
         default:
             return StringPrintf("Unknown %#08x", format);
     }
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 80f6c82..3fc99bb 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -634,6 +634,12 @@
                outSmpte2094_40);
 }
 
+status_t Gralloc4Mapper::getSmpte2094_10(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2094_10, gralloc4::decodeSmpte2094_10,
+               outSmpte2094_10);
+}
+
 template <class T>
 status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
                                     uint32_t layerCount, uint64_t usage,
@@ -935,9 +941,10 @@
     }
     double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
 
-    *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
-             << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
-             << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+    *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << std::fixed
+             << allocationSizeKiB << "KiB, w/h:" << width << "x" << height << ", usage: 0x"
+             << std::hex << usage << std::dec
+             << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
              << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
              << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) << std::dec
              << ", compressed: ";
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index d20bd7a..82d6cd5 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -301,6 +301,11 @@
     return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
 }
 
+status_t GraphicBufferMapper::getSmpte2094_10(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+    return mMapper->getSmpte2094_10(bufferHandle, outSmpte2094_10);
+}
+
 status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
                                                           PixelFormat format, uint32_t layerCount,
                                                           uint64_t usage,
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index e88fdd5..799fbc9 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -35,25 +35,8 @@
         case PIXEL_FORMAT_RGBA_5551:
         case PIXEL_FORMAT_RGBA_4444:
             return 2;
-    }
-    return 0;
-}
-
-uint32_t bitsPerPixel(PixelFormat format) {
-    switch (format) {
-        case PIXEL_FORMAT_RGBA_FP16:
-            return 64;
-        case PIXEL_FORMAT_RGBA_8888:
-        case PIXEL_FORMAT_RGBX_8888:
-        case PIXEL_FORMAT_BGRA_8888:
-        case PIXEL_FORMAT_RGBA_1010102:
-            return 32;
-        case PIXEL_FORMAT_RGB_888:
-            return 24;
-        case PIXEL_FORMAT_RGB_565:
-        case PIXEL_FORMAT_RGBA_5551:
-        case PIXEL_FORMAT_RGBA_4444:
-            return 16;
+        case PIXEL_FORMAT_R_8:
+            return 1;
     }
     return 0;
 }
diff --git a/libs/ui/StaticAsserts.cpp b/libs/ui/StaticAsserts.cpp
new file mode 100644
index 0000000..85da64f
--- /dev/null
+++ b/libs/ui/StaticAsserts.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/PixelFormat.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+
+// Ideally, PIXEL_FORMAT_R_8 would simply be defined to match the aidl PixelFormat, but
+// PixelFormat.h (where PIXEL_FORMAT_R_8 is defined) is pulled in by builds for
+// which there is no aidl build (e.g. Windows).
+static_assert(android::PIXEL_FORMAT_R_8 ==static_cast<int32_t>(
+                                  aidl::android::hardware::graphics::common::PixelFormat::R_8));
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index cd68c1c..b34d906 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -396,6 +396,11 @@
         result.mMatrix[1][0] = -b*idet;
         result.mMatrix[1][1] =  a*idet;
         result.mType = mType;
+        if (getOrientation() & ROT_90) {
+            // Recalculate the type if there is a 90-degree rotation component, since the inverse
+            // of ROT_90 is ROT_270 and vice versa.
+            result.mType |= UNKNOWN_TYPE;
+        }
 
         vec2 T(-x, -y);
         T = result.transform(T);
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index f196ab9..9120972 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -38,12 +38,22 @@
 
     uint64_t value;
 
+    // For deserialization.
+    static constexpr std::optional<DisplayId> fromValue(uint64_t);
+
+    // As above, but also upcast to Id.
+    template <typename Id>
+    static constexpr std::optional<Id> fromValue(uint64_t value) {
+        if (const auto id = Id::tryCast(DisplayId(value))) {
+            return id;
+        }
+        return {};
+    }
+
 protected:
     explicit constexpr DisplayId(uint64_t id) : value(id) {}
 };
 
-static_assert(sizeof(DisplayId) == sizeof(uint64_t));
-
 inline bool operator==(DisplayId lhs, DisplayId rhs) {
     return lhs.value == rhs.value;
 }
@@ -80,11 +90,8 @@
 
     // TODO(b/162612135) Remove default constructor
     PhysicalDisplayId() = default;
-    // TODO(b/162612135) Remove constructor
-    explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
 
     constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
-
     constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
 
 private:
@@ -96,10 +103,9 @@
     explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
-static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
-
 struct VirtualDisplayId : DisplayId {
     using BaseId = uint32_t;
+
     // Flag indicating that this virtual display is backed by the GPU.
     static constexpr uint64_t FLAG_GPU = 1ULL << 61;
 
@@ -163,10 +169,23 @@
     explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
+constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) {
+    if (const auto id = fromValue<PhysicalDisplayId>(value)) {
+        return id;
+    }
+    if (const auto id = fromValue<VirtualDisplayId>(value)) {
+        return id;
+    }
+    return {};
+}
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
-static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
 
 } // namespace android
 
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 70a0d50..98ee356 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -16,21 +16,18 @@
 
 #pragma once
 
+#include <ui/LayerStack.h>
 #include <ui/Rotation.h>
 #include <ui/Size.h>
 
-#include <cstdint>
 #include <type_traits>
 
 namespace android::ui {
 
-using LayerStack = uint32_t;
-constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
-
 // Transactional state of physical or virtual display. Note that libgui defines
 // android::DisplayState as a superset of android::ui::DisplayState.
 struct DisplayState {
-    LayerStack layerStack = NO_LAYER_STACK;
+    LayerStack layerStack;
     Rotation orientation = ROTATION_0;
     Size layerStackSpaceRect;
 };
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 6efecd3..9aae145 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -26,6 +26,10 @@
 
 namespace android {
 
+namespace mock {
+class MockFence;
+}
+
 class String8;
 
 // ===========================================================================
@@ -109,7 +113,7 @@
     // fence transitioned to the signaled state.  If the fence is not signaled
     // then SIGNAL_TIME_PENDING is returned.  If the fence is invalid or if an
     // error occurs then SIGNAL_TIME_INVALID is returned.
-    nsecs_t getSignalTime() const;
+    virtual nsecs_t getSignalTime() const;
 
     enum class Status {
         Invalid,     // Fence is invalid
@@ -120,7 +124,7 @@
     // getStatus() returns whether the fence has signaled yet. Prefer this to
     // getSignalTime() or wait() if all you care about is whether the fence has
     // signaled.
-    inline Status getStatus() {
+    virtual inline Status getStatus() {
         // The sync_wait call underlying wait() has been measured to be
         // significantly faster than the sync_fence_info call underlying
         // getSignalTime(), which might otherwise appear to be the more obvious
@@ -144,7 +148,10 @@
 private:
     // Only allow instantiation using ref counting.
     friend class LightRefBase<Fence>;
-    ~Fence() = default;
+    virtual ~Fence() = default;
+
+    // Allow mocking for unit testing
+    friend class mock::MockFence;
 
     base::unique_fd mFenceFd;
 };
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index e199648..753b0a6 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -178,6 +178,11 @@
             std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
         return INVALID_OPERATION;
     }
+    virtual status_t getSmpte2094_10(
+            buffer_handle_t /*bufferHandle*/,
+            std::optional<std::vector<uint8_t>>* /*outSmpte2094_10*/) const {
+        return INVALID_OPERATION;
+    }
 
     virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
                                                  PixelFormat /*format*/, uint32_t /*layerCount*/,
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 4729cba..62f9e4a 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -108,6 +108,8 @@
                          std::optional<ui::Cta861_3>* outCta861_3) const override;
     status_t getSmpte2094_40(buffer_handle_t bufferHandle,
                              std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+    status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_10) const override;
 
     status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
                                          uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 837e3d8..257c155 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -126,6 +126,8 @@
     status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
     status_t getSmpte2094_40(buffer_handle_t bufferHandle,
                              std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+    status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_10);
 
     /**
      * Gets the default metadata for a gralloc buffer allocated with the given parameters.
diff --git a/libs/ui/include/ui/LayerStack.h b/libs/ui/include/ui/LayerStack.h
new file mode 100644
index 0000000..d6ffeb7
--- /dev/null
+++ b/libs/ui/include/ui/LayerStack.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <ftl/cast.h>
+#include <ftl/string.h>
+#include <log/log.h>
+
+namespace android::ui {
+
+// A LayerStack identifies a Z-ordered group of layers. A layer can only be associated to a single
+// LayerStack, but a LayerStack can be associated to multiple displays, mirroring the same content.
+struct LayerStack {
+    uint32_t id = UINT32_MAX;
+
+    template <typename T>
+    static constexpr LayerStack fromValue(T v) {
+        if (ftl::cast_safety<uint32_t>(v) == ftl::CastSafety::kSafe) {
+            return {static_cast<uint32_t>(v)};
+        }
+
+        ALOGW("Invalid layer stack %s", ftl::to_string(v).c_str());
+        return {};
+    }
+};
+
+constexpr LayerStack INVALID_LAYER_STACK;
+constexpr LayerStack DEFAULT_LAYER_STACK{0u};
+
+inline bool operator==(LayerStack lhs, LayerStack rhs) {
+    return lhs.id == rhs.id;
+}
+
+inline bool operator!=(LayerStack lhs, LayerStack rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator>(LayerStack lhs, LayerStack rhs) {
+    return lhs.id > rhs.id;
+}
+
+// A LayerFilter determines if a layer is included for output to a display.
+struct LayerFilter {
+    LayerStack layerStack;
+
+    // True if the layer is only output to internal displays, i.e. excluded from screenshots, screen
+    // recordings, and mirroring to virtual or external displays. Used for display cutout overlays.
+    bool toInternalDisplay = false;
+
+    // Returns true if the input filter can be output to this filter.
+    bool includes(LayerFilter other) const {
+        // The layer stacks must match.
+        if (other.layerStack == INVALID_LAYER_STACK || other.layerStack != layerStack) {
+            return false;
+        }
+
+        // The output must be to an internal display if the input filter has that constraint.
+        return !other.toInternalDisplay || toInternalDisplay;
+    }
+};
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index 02773d9..f422ce4 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -62,12 +62,12 @@
     PIXEL_FORMAT_RGBA_4444    = 7,                             // 16-bit ARGB
     PIXEL_FORMAT_RGBA_FP16    = HAL_PIXEL_FORMAT_RGBA_FP16,    // 64-bit RGBA
     PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
+    PIXEL_FORMAT_R_8          = 0x38,
 };
 
 typedef int32_t PixelFormat;
 
 uint32_t bytesPerPixel(PixelFormat format);
-uint32_t bitsPerPixel(PixelFormat format);
 
 }; // namespace android
 
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index f1e8252..ecc192d 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -23,103 +23,70 @@
 #include <type_traits>
 #include <utility>
 
-namespace android {
-namespace ui {
+namespace android::ui {
 
-// Forward declare a few things.
-struct Size;
-bool operator==(const Size& lhs, const Size& rhs);
-
-/**
- * A simple value type representing a two-dimensional size
- */
+// A simple value type representing a two-dimensional size.
 struct Size {
-    int32_t width;
-    int32_t height;
+    int32_t width = -1;
+    int32_t height = -1;
 
-    // Special values
-    static const Size INVALID;
-    static const Size EMPTY;
+    constexpr Size() = default;
 
-    // ------------------------------------------------------------------------
-    // Construction
-    // ------------------------------------------------------------------------
-
-    Size() : Size(INVALID) {}
     template <typename T>
-    Size(T&& w, T&& h)
-          : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
-            height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
-
-    // ------------------------------------------------------------------------
-    // Accessors
-    // ------------------------------------------------------------------------
+    constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {}
 
     int32_t getWidth() const { return width; }
     int32_t getHeight() const { return height; }
 
-    template <typename T>
-    void setWidth(T&& v) {
-        width = Size::clamp<int32_t, T>(std::forward<T>(v));
-    }
-    template <typename T>
-    void setHeight(T&& v) {
-        height = Size::clamp<int32_t, T>(std::forward<T>(v));
-    }
-
-    // ------------------------------------------------------------------------
-    // Assignment
-    // ------------------------------------------------------------------------
-
-    void set(const Size& size) { *this = size; }
-    template <typename T>
-    void set(T&& w, T&& h) {
-        set(Size(std::forward<T>(w), std::forward<T>(h)));
-    }
-
-    // Sets the value to INVALID
-    void makeInvalid() { set(INVALID); }
-
-    // Sets the value to EMPTY
-    void clear() { set(EMPTY); }
-
-    // ------------------------------------------------------------------------
-    // Semantic checks
-    // ------------------------------------------------------------------------
-
     // Valid means non-negative width and height
     bool isValid() const { return width >= 0 && height >= 0; }
 
     // Empty means zero width and height
-    bool isEmpty() const { return *this == EMPTY; }
+    bool isEmpty() const;
 
-    // ------------------------------------------------------------------------
-    // Clamp Helpers
-    // ------------------------------------------------------------------------
-
-    // Note: We use only features available in C++11 here for compatibility with
-    // external targets which include this file directly or indirectly and which
-    // themselves use C++11.
-
-    // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
     template <typename T>
-    using remove_cv_reference_t =
-            typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+    void setWidth(T v) {
+        width = clamp<int32_t>(v);
+    }
+
+    template <typename T>
+    void setHeight(T v) {
+        height = clamp<int32_t>(v);
+    }
+
+    void set(Size size) { *this = size; }
+
+    template <typename T>
+    void set(T w, T h) {
+        set(Size(w, h));
+    }
+
+    // Sets the value to kInvalidSize
+    void makeInvalid();
+
+    // Sets the value to kEmptySize
+    void clear();
+
+    // TODO: Replace with std::remove_cvref_t in C++20.
+    template <typename T>
+    using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
 
     // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
     // clamping the input value to the output range if necessary.
     template <typename ToType, typename FromType>
-    static Size::remove_cv_reference_t<ToType>
-    clamp(typename std::enable_if<
-            std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
-                    std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
-            FromType>::type v) {
-        using BareToType = remove_cv_reference_t<ToType>;
-        using BareFromType = remove_cv_reference_t<FromType>;
-        static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
-        static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
-        static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
-        static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
+    static constexpr remove_cvref_t<ToType> clamp(FromType v) {
+        using BareToType = remove_cvref_t<ToType>;
+        using ToLimits = std::numeric_limits<BareToType>;
+
+        using BareFromType = remove_cvref_t<FromType>;
+        using FromLimits = std::numeric_limits<BareFromType>;
+
+        static_assert(ToLimits::is_specialized && FromLimits::is_specialized);
+
+        constexpr auto toHighest = ToLimits::max();
+        constexpr auto toLowest = ToLimits::lowest();
+        constexpr auto fromHighest = FromLimits::max();
+        constexpr auto fromLowest = FromLimits::lowest();
 
         // Get the closest representation of [toLowest, toHighest] in type
         // FromType to use to clamp the input value before conversion.
@@ -127,37 +94,35 @@
         // std::common_type<...> is used to get a value-preserving type for the
         // top end of the range.
         using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+        using CommonLimits = std::numeric_limits<CommonHighestType>;
 
         // std::make_signed<std::common_type<...>> is used to get a
         // value-preserving type for the bottom end of the range, except this is
         // a bit trickier for non-integer types like float.
-        using CommonLowestType =
-                std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
-                                   std::make_signed_t<std::conditional_t<
-                                           std::numeric_limits<CommonHighestType>::is_integer,
-                                           CommonHighestType, int /* not used */>>,
-                                   CommonHighestType>;
+        using CommonLowestType = std::conditional_t<
+                CommonLimits::is_integer,
+                std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType,
+                                                      int /* not used */>>,
+                CommonHighestType>;
 
         // We can then compute the clamp range in a way that can be later
         // trivially converted to either the 'from' or 'to' types, and be
-        // representabile in either.
-        static constexpr auto commonClampHighest =
-                std::min(static_cast<CommonHighestType>(fromHighest),
-                         static_cast<CommonHighestType>(toHighest));
-        static constexpr auto commonClampLowest =
-                std::max(static_cast<CommonLowestType>(fromLowest),
-                         static_cast<CommonLowestType>(toLowest));
+        // representable in either.
+        constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest),
+                                                     static_cast<CommonHighestType>(toHighest));
+        constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest),
+                                                    static_cast<CommonLowestType>(toLowest));
 
-        static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
-        static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+        constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+        constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
 
         // A clamp is needed only if the range we are clamping to is not the
         // same as the range of the input.
-        static constexpr bool isClampNeeded =
+        constexpr bool isClampNeeded =
                 (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
 
         // If a clamp is not needed, the conversion is just a trivial cast.
-        if (!isClampNeeded) {
+        if constexpr (!isClampNeeded) {
             return static_cast<BareToType>(v);
         }
 
@@ -170,34 +135,46 @@
 
         // Otherwise clamping is done by using the already computed endpoints
         // for each type.
-        return (v <= fromClampLowest)
-                ? toClampLowest
-                : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
+        if (v <= fromClampLowest) {
+            return toClampLowest;
+        }
+
+        return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v);
     }
 };
 
-// ------------------------------------------------------------------------
-// Comparisons
-// ------------------------------------------------------------------------
+constexpr Size kInvalidSize;
+constexpr Size kEmptySize{0, 0};
 
-inline bool operator==(const Size& lhs, const Size& rhs) {
+inline void Size::makeInvalid() {
+    set(kInvalidSize);
+}
+
+inline void Size::clear() {
+    set(kEmptySize);
+}
+
+inline bool operator==(Size lhs, Size rhs) {
     return lhs.width == rhs.width && lhs.height == rhs.height;
 }
 
-inline bool operator!=(const Size& lhs, const Size& rhs) {
-    return !operator==(lhs, rhs);
+inline bool Size::isEmpty() const {
+    return *this == kEmptySize;
 }
 
-inline bool operator<(const Size& lhs, const Size& rhs) {
+inline bool operator!=(Size lhs, Size rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator<(Size lhs, Size rhs) {
     // Orders by increasing width, then height.
     if (lhs.width != rhs.width) return lhs.width < rhs.width;
     return lhs.height < rhs.height;
 }
 
 // Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Size& size, ::std::ostream* os) {
-    *os << "Size(" << size.width << ", " << size.height << ")";
+inline void PrintTo(Size size, std::ostream* stream) {
+    *stream << "Size(" << size.width << ", " << size.height << ')';
 }
 
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include_mock/ui/MockFence.h b/libs/ui/include_mock/ui/MockFence.h
new file mode 100644
index 0000000..71adee4
--- /dev/null
+++ b/libs/ui/include_mock/ui/MockFence.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+#include <ui/Fence.h>
+
+namespace android::mock {
+
+class MockFence : public android::Fence {
+public:
+    MockFence() = default;
+    virtual ~MockFence() = default;
+
+    MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override));
+    MOCK_METHOD(Status, getStatus, (), (override));
+};
+
+}; // namespace android::mock
diff --git a/libs/ui/Size.cpp b/libs/ui/include_types/ui/DataspaceUtils.h
similarity index 61%
copy from libs/ui/Size.cpp
copy to libs/ui/include_types/ui/DataspaceUtils.h
index d2996d1..a461cb4 100644
--- a/libs/ui/Size.cpp
+++ b/libs/ui/include_types/ui/DataspaceUtils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+#pragma once
 
-namespace android::ui {
+#include <ui/GraphicTypes.h>
 
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+namespace android {
 
-} // namespace android::ui
+inline bool isHdrDataspace(ui::Dataspace dataspace) {
+    const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+    return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include_types/ui/GraphicTypes.h b/libs/ui/include_types/ui/GraphicTypes.h
new file mode 120000
index 0000000..b1859e0
--- /dev/null
+++ b/libs/ui/include_types/ui/GraphicTypes.h
@@ -0,0 +1 @@
+../../include/ui/GraphicTypes.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 516aad8..22fbf45 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -27,28 +27,40 @@
     name: "Region_test",
     shared_libs: ["libui"],
     srcs: ["Region_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test {
     name: "colorspace_test",
     shared_libs: ["libui"],
     srcs: ["colorspace_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test {
     name: "DisplayId_test",
     shared_libs: ["libui"],
     srcs: ["DisplayId_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test {
     name: "FlattenableHelpers_test",
     shared_libs: ["libui"],
     srcs: ["FlattenableHelpers_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test {
@@ -68,7 +80,10 @@
         "GraphicBufferAllocator_test.cpp",
         "mock/MockGrallocAllocator.cpp",
     ],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test {
@@ -83,14 +98,20 @@
         "libutils",
     ],
     srcs: ["GraphicBuffer_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 // This test has a main method, and requires a separate binary to be built.
 cc_test {
     name: "GraphicBufferOverBinder_test",
     srcs: ["GraphicBufferOverBinder_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbinder",
         "libgui",
@@ -105,7 +126,10 @@
     test_suites: ["device-tests"],
     shared_libs: ["libui"],
     srcs: ["Rect_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test {
@@ -113,5 +137,39 @@
     test_suites: ["device-tests"],
     shared_libs: ["libui"],
     srcs: ["Size_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "MockFence_test",
+    shared_libs: ["libui"],
+    static_libs: ["libgmock"],
+    srcs: ["MockFence_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "Transform_test",
+    shared_libs: ["libui"],
+    srcs: ["Transform_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "DataspaceUtils_test",
+    shared_libs: ["libui"],
+    srcs: ["DataspaceUtils_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
new file mode 100644
index 0000000..3e09671
--- /dev/null
+++ b/libs/ui/tests/DataspaceUtils_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DataspaceUtilsTest"
+
+#include <gtest/gtest.h>
+#include <ui/DataspaceUtils.h>
+
+namespace android {
+
+class DataspaceUtilsTest : public testing::Test {};
+
+TEST_F(DataspaceUtilsTest, isHdrDataspace) {
+    EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_HLG));
+    EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ));
+    EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ));
+    EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG));
+
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR));
+    // scRGB defines a very wide gamut but not an expanded luminance range
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT709));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3_LINEAR));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3_LINEAR));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::ADOBE_RGB));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_LINEAR));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_ITU));
+    EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_BT2020));
+}
+
+} // namespace android
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index 1d908b8..8ddee7e 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -32,6 +32,9 @@
     EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
     EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
     EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
 }
 
 TEST(DisplayIdTest, createPhysicalIdFromPort) {
@@ -43,6 +46,9 @@
     EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
     EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
     EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
 }
 
 TEST(DisplayIdTest, createGpuVirtualId) {
@@ -52,6 +58,9 @@
     EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
     EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
     EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
 }
 
 TEST(DisplayIdTest, createHalVirtualId) {
@@ -61,6 +70,9 @@
     EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
     EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
     EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
 }
 
 } // namespace android::ui
diff --git a/libs/ui/tests/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp
new file mode 100644
index 0000000..40dddc3
--- /dev/null
+++ b/libs/ui/tests/MockFence_test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/MockFence.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+using testing::Return;
+
+class MockFenceTest : public testing::Test {
+public:
+    sp<Fence> getFenceForTesting() const { return mMockFence; }
+
+    const mock::MockFence& getMockFence() const { return *mMockFence; }
+
+private:
+    sp<mock::MockFence> mMockFence = sp<mock::MockFence>::make();
+};
+
+TEST_F(MockFenceTest, getSignalTime) {
+    sp<Fence> fence = getFenceForTesting();
+
+    EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(Fence::SIGNAL_TIME_PENDING));
+    EXPECT_EQ(Fence::SIGNAL_TIME_PENDING, fence->getSignalTime());
+
+    EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(1234));
+    EXPECT_EQ(1234, fence->getSignalTime());
+}
+
+TEST_F(MockFenceTest, getStatus) {
+    sp<Fence> fence = getFenceForTesting();
+
+    EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Unsignaled));
+    EXPECT_EQ(Fence::Status::Unsignaled, fence->getStatus());
+
+    EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Signaled));
+    EXPECT_EQ(Fence::Status::Signaled, fence->getStatus());
+
+    EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Invalid));
+    EXPECT_EQ(Fence::Status::Invalid, fence->getStatus());
+}
+} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 5f75aea..acef47f 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -93,9 +93,8 @@
     }
 
     {
-        const auto& s = Size::INVALID;
-        EXPECT_FALSE(s.isValid());
-        EXPECT_FALSE(s.isEmpty());
+        EXPECT_FALSE(kInvalidSize.isValid());
+        EXPECT_FALSE(kInvalidSize.isEmpty());
     }
 
     {
@@ -112,9 +111,8 @@
     }
 
     {
-        const auto& s = Size::EMPTY;
-        EXPECT_TRUE(s.isValid());
-        EXPECT_TRUE(s.isEmpty());
+        EXPECT_TRUE(kEmptySize.isValid());
+        EXPECT_TRUE(kEmptySize.isEmpty());
     }
 
     {
diff --git a/libs/ui/tests/Transform_test.cpp b/libs/ui/tests/Transform_test.cpp
new file mode 100644
index 0000000..6964284
--- /dev/null
+++ b/libs/ui/tests/Transform_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(TransformTest, inverseRotation_hasCorrectType) {
+    const auto testRotationFlagsForInverse = [](Transform::RotationFlags rotation,
+                                                Transform::RotationFlags expectedInverse,
+                                                bool isRotation) {
+        const Transform t(rotation, 0, 0);
+        EXPECT_EQ(t.getOrientation(), rotation);
+        const Transform inverse = t.inverse();
+        EXPECT_EQ(inverse.getOrientation(), expectedInverse);
+
+        if (isRotation) {
+            EXPECT_TRUE(t.getType() & Transform::ROTATE);
+            EXPECT_TRUE(inverse.getType() & Transform::ROTATE);
+        } else {
+            EXPECT_FALSE(t.getType() & Transform::ROTATE);
+            EXPECT_FALSE(inverse.getType() & Transform::ROTATE);
+        }
+    };
+
+    testRotationFlagsForInverse(Transform::ROT_0, Transform::ROT_0, false);
+    testRotationFlagsForInverse(Transform::ROT_90, Transform::ROT_270, true);
+    testRotationFlagsForInverse(Transform::ROT_180, Transform::ROT_180, true);
+    testRotationFlagsForInverse(Transform::ROT_270, Transform::ROT_90, true);
+    testRotationFlagsForInverse(Transform::FLIP_H, Transform::FLIP_H, false);
+    testRotationFlagsForInverse(Transform::FLIP_V, Transform::FLIP_V, false);
+}
+
+} // namespace android::ui
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
deleted file mode 100644
index bf848af..0000000
--- a/libs/vr/libvrflinger/Android.bp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
-    "acquired_buffer.cpp",
-    "epoll_event_dispatcher.cpp",
-    "display_manager_service.cpp",
-    "display_service.cpp",
-    "display_surface.cpp",
-    "hardware_composer.cpp",
-    "vr_flinger.cpp",
-]
-
-includeFiles = [ "include" ]
-
-staticLibraries = [
-    "libdisplay",
-    "libdvrcommon",
-    "libperformance",
-    "libvrsensor",
-    "libbroadcastring",
-    "libvr_manager",
-    "libbroadcastring",
-]
-
-sharedLibraries = [
-    "android.frameworks.vr.composer@2.0",
-    "android.hardware.graphics.allocator@2.0",
-    "android.hardware.graphics.composer@2.1",
-    "android.hardware.graphics.composer@2.2",
-    "android.hardware.graphics.composer@2.3",
-    "android.hardware.graphics.composer@2.4",
-    "libbinder",
-    "libbase",
-    "libbufferhubqueue",
-    "libcutils",
-    "liblog",
-    "libhardware",
-    "libnativewindow",
-    "libprocessgroup",
-    "libutils",
-    "libEGL",
-    "libGLESv1_CM",
-    "libGLESv2",
-    "libvulkan",
-    "libui",
-    "libgui",
-    "libsync",
-    "libhidlbase",
-    "libfmq",
-    "libpdx_default_transport",
-]
-
-headerLibraries = [
-    "android.hardware.graphics.composer@2.1-command-buffer",
-    "android.hardware.graphics.composer@2.2-command-buffer",
-    "android.hardware.graphics.composer@2.3-command-buffer",
-    "android.hardware.graphics.composer@2.4-command-buffer",
-    "libdvr_headers",
-    "libsurfaceflinger_headers",
-]
-
-cc_library_static {
-    srcs: sourceFiles,
-    export_include_dirs: includeFiles,
-
-    clang: true,
-    cflags: [
-        "-DLOG_TAG=\"vr_flinger\"",
-        "-DTRACE=0",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-DGL_GLEXT_PROTOTYPES",
-        "-DEGL_EGLEXT_PROTOTYPES",
-        "-Wall",
-        "-Werror",
-        "-Wno-error=sign-compare", // to fix later
-        "-Wno-unused-variable",
-    ],
-    shared_libs: sharedLibraries,
-    whole_static_libs: staticLibraries,
-    header_libs: headerLibraries,
-    name: "libvrflinger",
-}
-
-subdirs = [
-    "tests",
-]
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
deleted file mode 100644
index c360dee..0000000
--- a/libs/vr/libvrflinger/acquired_buffer.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "acquired_buffer.h"
-
-#include <log/log.h>
-#include <sync/sync.h>
-
-using android::pdx::LocalHandle;
-
-namespace android {
-namespace dvr {
-
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
-                               LocalHandle acquire_fence, std::size_t slot)
-    : buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {}
-
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
-                               int* error) {
-  LocalHandle fence;
-  const int ret = buffer->Acquire(&fence);
-
-  if (error)
-    *error = ret;
-
-  if (ret < 0) {
-    ALOGW("AcquiredBuffer::AcquiredBuffer: Failed to acquire buffer: %s",
-          strerror(-ret));
-    buffer_ = nullptr;
-    // Default construct sets acquire_fence_ to empty.
-  } else {
-    buffer_ = buffer;
-    acquire_fence_ = std::move(fence);
-  }
-}
-
-AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) noexcept {
-  *this = std::move(other);
-}
-
-AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
-
-AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) noexcept {
-  if (this != &other) {
-    Release();
-
-    using std::swap;
-    swap(buffer_, other.buffer_);
-    swap(acquire_fence_, other.acquire_fence_);
-    swap(slot_, other.slot_);
-  }
-  return *this;
-}
-
-bool AcquiredBuffer::IsAvailable() const {
-  if (IsEmpty())
-    return false;
-
-  // Only check the fence if the acquire fence is not empty.
-  if (acquire_fence_) {
-    const int ret = sync_wait(acquire_fence_.Get(), 0);
-    ALOGD_IF(TRACE || (ret < 0 && errno != ETIME),
-             "AcquiredBuffer::IsAvailable: buffer_id=%d acquire_fence=%d "
-             "sync_wait()=%d errno=%d.",
-             buffer_->id(), acquire_fence_.Get(), ret, ret < 0 ? errno : 0);
-    if (ret == 0) {
-      // The fence is completed, so to avoid further calls to sync_wait we close
-      // it here.
-      acquire_fence_.Close();
-    }
-    return ret == 0;
-  } else {
-    return true;
-  }
-}
-
-LocalHandle AcquiredBuffer::ClaimAcquireFence() {
-  return std::move(acquire_fence_);
-}
-
-std::shared_ptr<ConsumerBuffer> AcquiredBuffer::ClaimBuffer() {
-  return std::move(buffer_);
-}
-
-int AcquiredBuffer::Release(LocalHandle release_fence) {
-  ALOGD_IF(TRACE, "AcquiredBuffer::Release: buffer_id=%d release_fence=%d",
-           buffer_ ? buffer_->id() : -1, release_fence.Get());
-  if (buffer_) {
-    const int ret = buffer_->ReleaseAsync();
-    if (ret < 0) {
-      ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s",
-            buffer_->id(), strerror(-ret));
-      if (ret != -ESHUTDOWN)
-        return ret;
-    }
-
-    buffer_ = nullptr;
-  }
-
-  acquire_fence_.Close();
-  slot_ = 0;
-  return 0;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
deleted file mode 100644
index 7643e75..0000000
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
-
-#include <pdx/file_handle.h>
-#include <private/dvr/consumer_buffer.h>
-
-#include <memory>
-
-namespace android {
-namespace dvr {
-
-// Manages the ACQUIRE/RELEASE ownership cycle of a ConsumerBuffer.
-class AcquiredBuffer {
- public:
-  static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
-
-  AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
-
-  // Constructs an AcquiredBuffer from a ConsumerBuffer pointer and an acquire
-  // fence. The ConsumerBuffer MUST be in the ACQUIRED state prior to calling
-  // this constructor; the constructor does not attempt to ACQUIRE the buffer
-  // itself.
-  AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
-                 pdx::LocalHandle acquire_fence, std::size_t slot = 0);
-
-  // Constructs an AcquiredBuffer from a ConsumerBuffer. The ConsumerBuffer MUST
-  // be in the POSTED state prior to calling this constructor, as this
-  // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
-  // this instance is left in the empty state. An optional error code is
-  // returned in |error|, which may be nullptr if not needed.
-  AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer, int* error);
-
-  // Move constructor. Behaves similarly to the move assignment operator below.
-  AcquiredBuffer(AcquiredBuffer&& other) noexcept;
-
-  ~AcquiredBuffer();
-
-  // Move assignment operator. Moves the ConsumerBuffer and acquire fence from
-  // |other| into this instance after RELEASING the current ConsumerBuffer and
-  // closing the acquire fence. After the move |other| is left in the empty
-  // state.
-  AcquiredBuffer& operator=(AcquiredBuffer&& other) noexcept;
-
-  // Accessors for the underlying ConsumerBuffer, the acquire fence, and the
-  // use-case specific sequence value from the acquisition (see
-  // private/dvr/consumer_buffer.h).
-  std::shared_ptr<ConsumerBuffer> buffer() const { return buffer_; }
-  int acquire_fence() const { return acquire_fence_.Get(); }
-
-  // When non-empty, returns true if the acquired fence was signaled (or if the
-  // fence is empty). Returns false when empty or if the fence is not signaled.
-  bool IsAvailable() const;
-
-  bool IsEmpty() const { return buffer_ == nullptr; }
-
-  // Returns the acquire fence, passing ownership to the caller.
-  pdx::LocalHandle ClaimAcquireFence();
-
-  // Returns the buffer, passing ownership to the caller. Caller is responsible
-  // for calling Release on the returned buffer.
-  std::shared_ptr<ConsumerBuffer> ClaimBuffer();
-
-  // Releases the ConsumerBuffer, passing the release fence in |release_fence|
-  // to the producer. On success, the ConsumerBuffer and acquire fence are set
-  // to empty state; if release fails, the ConsumerBuffer and acquire fence are
-  // left in place and a negative error code is returned.
-  int Release(pdx::LocalHandle release_fence = {});
-
-  // Returns the slot in the queue this buffer belongs to. Buffers that are not
-  // part of a queue return 0.
-  std::size_t slot() const { return slot_; }
-
- private:
-  std::shared_ptr<ConsumerBuffer> buffer_;
-  // Mutable so that the fence can be closed when it is determined to be
-  // signaled during IsAvailable().
-  mutable pdx::LocalHandle acquire_fence_;
-  std::size_t slot_{0};
-
-  AcquiredBuffer(const AcquiredBuffer&) = delete;
-  void operator=(const AcquiredBuffer&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
deleted file mode 100644
index 34b3b0a..0000000
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "display_manager_service.h"
-
-#include <pdx/channel_handle.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <private/android_filesystem_config.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/trusted_uids.h>
-#include <sys/poll.h>
-
-#include <array>
-
-using android::dvr::display::DisplayManagerProtocol;
-using android::pdx::Channel;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Message;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::ErrorStatus;
-using android::pdx::rpc::DispatchRemoteMethod;
-using android::pdx::rpc::IfAnyOf;
-using android::pdx::rpc::RemoteMethodError;
-
-namespace android {
-namespace dvr {
-
-void DisplayManager::SetNotificationsPending(bool pending) {
-  auto status = service_->ModifyChannelEvents(channel_id_, pending ? 0 : POLLIN,
-                                              pending ? POLLIN : 0);
-  ALOGE_IF(!status,
-           "DisplayManager::SetNotificationPending: Failed to modify channel "
-           "events: %s",
-           status.GetErrorMessage().c_str());
-}
-
-DisplayManagerService::DisplayManagerService(
-    const std::shared_ptr<DisplayService>& display_service)
-    : BASE("DisplayManagerService",
-           Endpoint::Create(DisplayManagerProtocol::kClientPath)),
-      display_service_(display_service) {
-  display_service_->SetDisplayConfigurationUpdateNotifier(
-      std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this));
-}
-
-std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen(
-    pdx::Message& message) {
-  const int user_id = message.GetEffectiveUserId();
-  const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
-
-  // Check if the display_manager_ has a defunct channel.
-  if (display_manager_ && !HasChannelId(display_manager_->channel_id())) {
-    ALOGE("DisplayManagerService::OnChannelOpen: Found defunct channel %d with "
-          "no OnChannelClose, clearing prior display manager.",
-          display_manager_->channel_id());
-    display_manager_ = nullptr;
-  }
-
-  // Prevent more than one display manager from registering at a time or
-  // untrusted UIDs from connecting.
-  if (display_manager_ || !trusted) {
-    RemoteMethodError(message, EPERM);
-    return nullptr;
-  }
-
-  display_manager_ =
-      std::make_shared<DisplayManager>(this, message.GetChannelId());
-  return display_manager_;
-}
-
-void DisplayManagerService::OnChannelClose(
-    pdx::Message& /*message*/, const std::shared_ptr<pdx::Channel>& channel) {
-  // Unregister the display manager when the channel closes.
-  if (display_manager_ == channel)
-    display_manager_ = nullptr;
-}
-
-pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) {
-  ATRACE_NAME("DisplayManagerService::HandleMessage");
-  auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
-
-  switch (message.GetOp()) {
-    case DisplayManagerProtocol::GetSurfaceState::Opcode:
-      DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(
-          *this, &DisplayManagerService::OnGetSurfaceState, message);
-      return {};
-
-    case DisplayManagerProtocol::GetSurfaceQueue::Opcode:
-      DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
-          *this, &DisplayManagerService::OnGetSurfaceQueue, message);
-      return {};
-
-    default:
-      return Service::DefaultHandleMessage(message);
-  }
-}
-
-pdx::Status<std::vector<display::SurfaceState>>
-DisplayManagerService::OnGetSurfaceState(pdx::Message& /*message*/) {
-  std::vector<display::SurfaceState> items;
-
-  display_service_->ForEachDisplaySurface(
-      SurfaceType::Application,
-      [&items](const std::shared_ptr<DisplaySurface>& surface) mutable {
-        items.push_back({surface->surface_id(), surface->process_id(),
-                         surface->user_id(), surface->attributes(),
-                         surface->update_flags(), surface->GetQueueIds()});
-        surface->ClearUpdate();
-      });
-
-  // The fact that we're in the message handler implies that display_manager_ is
-  // not nullptr. No check required, unless this service becomes multi-threaded.
-  display_manager_->SetNotificationsPending(false);
-  return items;
-}
-
-pdx::Status<pdx::LocalChannelHandle> DisplayManagerService::OnGetSurfaceQueue(
-    pdx::Message& /*message*/, int surface_id, int queue_id) {
-  auto surface = display_service_->GetDisplaySurface(surface_id);
-  if (!surface || surface->surface_type() != SurfaceType::Application)
-    return ErrorStatus(EINVAL);
-
-  auto queue =
-      std::static_pointer_cast<ApplicationDisplaySurface>(surface)->GetQueue(
-          queue_id);
-  if (!queue)
-    return ErrorStatus(EINVAL);
-
-  auto status = queue->CreateConsumerQueueHandle();
-  ALOGE_IF(
-      !status,
-      "DisplayManagerService::OnGetSurfaceQueue: Failed to create consumer "
-      "queue for queue_id=%d: %s",
-      queue->id(), status.GetErrorMessage().c_str());
-
-  return status;
-}
-
-void DisplayManagerService::OnDisplaySurfaceChange() {
-  if (display_manager_)
-    display_manager_->SetNotificationsPending(true);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
deleted file mode 100644
index 3133fe1..0000000
--- a/libs/vr/libvrflinger/display_manager_service.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
-#define ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
-
-#include <pdx/service.h>
-#include <pdx/status.h>
-#include <private/dvr/display_protocol.h>
-
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-class DisplayManagerService;
-
-// The display manager is a client of the display manager service. This class
-// represents the connected client that the display manager service sends
-// notifications to.
-class DisplayManager : public pdx::Channel {
- public:
-  DisplayManager(DisplayManagerService* service, int channel_id)
-      : service_(service), channel_id_(channel_id) {}
-
-  int channel_id() const { return channel_id_; }
-
-  // Sets or clears the channel event mask to indicate pending events that the
-  // display manager on the other end of the channel should read and handle.
-  // When |pending| is true the POLLIN bit is set in the event mask; when
-  // |pending| is false the POLLIN bit is cleared in the event mask.
-  void SetNotificationsPending(bool pending);
-
- private:
-  DisplayManager(const DisplayManager&) = delete;
-  void operator=(const DisplayManager&) = delete;
-
-  DisplayManagerService* service_;
-  int channel_id_;
-};
-
-// The display manager service marshalls state and events from the display
-// service to the display manager.
-class DisplayManagerService : public pdx::ServiceBase<DisplayManagerService> {
- public:
-  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
-  void OnChannelClose(pdx::Message& message,
-                      const std::shared_ptr<pdx::Channel>& channel) override;
-  pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
- private:
-  friend BASE;
-
-  explicit DisplayManagerService(
-      const std::shared_ptr<DisplayService>& display_service);
-
-  pdx::Status<std::vector<display::SurfaceState>> OnGetSurfaceState(
-      pdx::Message& message);
-  pdx::Status<pdx::LocalChannelHandle> OnGetSurfaceQueue(pdx::Message& message,
-                                                         int surface_id,
-                                                         int queue_id);
-
-  // Called by the display service to indicate changes to display surfaces that
-  // the display manager should evaluate.
-  void OnDisplaySurfaceChange();
-
-  DisplayManagerService(const DisplayManagerService&) = delete;
-  void operator=(const DisplayManagerService&) = delete;
-
-  std::shared_ptr<DisplayService> display_service_;
-  std::shared_ptr<DisplayManager> display_manager_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
deleted file mode 100644
index 582fed3..0000000
--- a/libs/vr/libvrflinger/display_service.cpp
+++ /dev/null
@@ -1,437 +0,0 @@
-#include "display_service.h"
-
-#include <unistd.h>
-
-#include <algorithm>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <dvr/dvr_display_types.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <pdx/rpc/remote_method.h>
-#include <private/android_filesystem_config.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/numeric.h>
-#include <private/dvr/trusted_uids.h>
-#include <private/dvr/types.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
-
-using android::dvr::display::DisplayProtocol;
-using android::pdx::Channel;
-using android::pdx::ErrorStatus;
-using android::pdx::Message;
-using android::pdx::Status;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace {
-
-const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
-const char kDvrDeviceMetricsProperty[] = "ro.dvr.device_metrics";
-const char kDvrDeviceConfigProperty[] = "ro.dvr.device_configuration";
-
-}  // namespace
-
-namespace android {
-namespace dvr {
-
-DisplayService::DisplayService(Hwc2::Composer* hidl,
-                               hwc2_display_t primary_display_id,
-                               RequestDisplayCallback request_display_callback)
-    : BASE("DisplayService",
-           Endpoint::Create(display::DisplayProtocol::kClientPath)) {
-    hardware_composer_.Initialize(
-        hidl, primary_display_id, request_display_callback);
-}
-
-bool DisplayService::IsInitialized() const {
-  return BASE::IsInitialized() && hardware_composer_.IsInitialized();
-}
-
-std::string DisplayService::DumpState(size_t /*max_length*/) {
-  std::ostringstream stream;
-
-  auto surfaces = GetDisplaySurfaces();
-  std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
-    return a->surface_id() < b->surface_id();
-  });
-
-  stream << "Application Surfaces:" << std::endl;
-
-  size_t count = 0;
-  for (const auto& surface : surfaces) {
-    if (surface->surface_type() == SurfaceType::Application) {
-      stream << "Surface " << count++ << ":";
-      stream << " surface_id=" << surface->surface_id()
-             << " process_id=" << surface->process_id()
-             << " user_id=" << surface->user_id()
-             << " visible=" << surface->visible()
-             << " z_order=" << surface->z_order();
-
-      stream << " queue_ids=";
-      auto queue_ids = surface->GetQueueIds();
-      std::sort(queue_ids.begin(), queue_ids.end());
-      for (int32_t id : queue_ids) {
-        if (id != queue_ids[0])
-          stream << ",";
-        stream << id;
-      }
-      stream << std::endl;
-    }
-  }
-  stream << std::endl;
-
-  stream << "Direct Surfaces:" << std::endl;
-
-  count = 0;
-  for (const auto& surface : surfaces) {
-    if (surface->surface_type() == SurfaceType::Direct) {
-      stream << "Surface " << count++ << ":";
-      stream << " surface_id=" << surface->surface_id()
-             << " process_id=" << surface->process_id()
-             << " user_id=" << surface->user_id()
-             << " visible=" << surface->visible()
-             << " z_order=" << surface->z_order();
-
-      stream << " queue_ids=";
-      auto queue_ids = surface->GetQueueIds();
-      std::sort(queue_ids.begin(), queue_ids.end());
-      for (int32_t id : queue_ids) {
-        if (id != queue_ids[0])
-          stream << ",";
-        stream << id;
-      }
-      stream << std::endl;
-    }
-  }
-  stream << std::endl;
-
-  stream << hardware_composer_.Dump();
-  return stream.str();
-}
-
-void DisplayService::OnChannelClose(pdx::Message& message,
-                                    const std::shared_ptr<Channel>& channel) {
-  if (auto surface = std::static_pointer_cast<DisplaySurface>(channel)) {
-    surface->OnSetAttributes(message,
-                             {{display::SurfaceAttribute::Visible,
-                               display::SurfaceAttributeValue{false}}});
-  }
-}
-
-// First-level dispatch for display service messages. Directly handles messages
-// that are independent of the display surface (metrics, creation) and routes
-// surface-specific messages to the per-instance handlers.
-Status<void> DisplayService::HandleMessage(pdx::Message& message) {
-  ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp());
-  ATRACE_NAME("DisplayService::HandleMessage");
-
-  switch (message.GetOp()) {
-    case DisplayProtocol::GetMetrics::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::GetMetrics>(
-          *this, &DisplayService::OnGetMetrics, message);
-      return {};
-
-    case DisplayProtocol::GetConfigurationData::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::GetConfigurationData>(
-          *this, &DisplayService::OnGetConfigurationData, message);
-      return {};
-
-    case DisplayProtocol::GetDisplayIdentificationPort::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(
-          *this, &DisplayService::OnGetDisplayIdentificationPort, message);
-      return {};
-
-    case DisplayProtocol::CreateSurface::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
-          *this, &DisplayService::OnCreateSurface, message);
-      return {};
-
-    case DisplayProtocol::SetupGlobalBuffer::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(
-          *this, &DisplayService::OnSetupGlobalBuffer, message);
-      return {};
-
-    case DisplayProtocol::DeleteGlobalBuffer::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(
-          *this, &DisplayService::OnDeleteGlobalBuffer, message);
-      return {};
-
-    case DisplayProtocol::GetGlobalBuffer::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::GetGlobalBuffer>(
-          *this, &DisplayService::OnGetGlobalBuffer, message);
-      return {};
-
-    case DisplayProtocol::IsVrAppRunning::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::IsVrAppRunning>(
-          *this, &DisplayService::IsVrAppRunning, message);
-      return {};
-
-    // Direct the surface specific messages to the surface instance.
-    case DisplayProtocol::SetAttributes::Opcode:
-    case DisplayProtocol::CreateQueue::Opcode:
-    case DisplayProtocol::GetSurfaceInfo::Opcode:
-      return HandleSurfaceMessage(message);
-
-    default:
-      return Service::HandleMessage(message);
-  }
-}
-
-Status<display::Metrics> DisplayService::OnGetMetrics(
-    pdx::Message& /*message*/) {
-  const auto& params = hardware_composer_.GetPrimaryDisplayParams();
-  return {{static_cast<uint32_t>(params.width),
-           static_cast<uint32_t>(params.height),
-           static_cast<uint32_t>(params.dpi.x),
-           static_cast<uint32_t>(params.dpi.y),
-           static_cast<uint32_t>(params.vsync_period_ns),
-           0,
-           0,
-           0,
-           0.0,
-           {},
-           {}}};
-}
-
-pdx::Status<std::string> DisplayService::OnGetConfigurationData(
-    pdx::Message& /*message*/, display::ConfigFileType config_type) {
-  std::string property_name;
-  DisplayIdentificationData display_identification_data;
-  switch (config_type) {
-    case display::ConfigFileType::kLensMetrics:
-      property_name = kDvrLensMetricsProperty;
-      break;
-    case display::ConfigFileType::kDeviceMetrics:
-      property_name = kDvrDeviceMetricsProperty;
-      break;
-    case display::ConfigFileType::kDeviceConfiguration:
-      property_name = kDvrDeviceConfigProperty;
-      break;
-    case display::ConfigFileType::kDeviceEdid:
-      display_identification_data =
-          hardware_composer_.GetCurrentDisplayIdentificationData();
-      if (display_identification_data.size() == 0) {
-        return ErrorStatus(ENOENT);
-      }
-      return std::string(display_identification_data.begin(),
-                         display_identification_data.end());
-    default:
-      return ErrorStatus(EINVAL);
-  }
-  std::string file_path = base::GetProperty(property_name, "");
-  if (file_path.empty()) {
-    return ErrorStatus(ENOENT);
-  }
-
-  std::string data;
-  if (!base::ReadFileToString(file_path, &data)) {
-    return ErrorStatus(errno);
-  }
-
-  return std::move(data);
-}
-
-pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort(
-    pdx::Message& /*message*/) {
-  return hardware_composer_.GetCurrentDisplayPort();
-}
-
-// Creates a new DisplaySurface and associates it with this channel. This may
-// only be done once per channel.
-Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
-    pdx::Message& message, const display::SurfaceAttributes& attributes) {
-  // A surface may only be created once per channel.
-  if (message.GetChannel())
-    return ErrorStatus(EINVAL);
-
-  ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d",
-           message.GetChannelId());
-
-  // Use the channel id as the unique surface id.
-  const int surface_id = message.GetChannelId();
-  const int process_id = message.GetProcessId();
-  const int user_id = message.GetEffectiveUserId();
-
-  ALOGI_IF(TRACE,
-           "DisplayService::OnCreateSurface: surface_id=%d process_id=%d",
-           surface_id, process_id);
-
-  auto surface_status =
-      DisplaySurface::Create(this, surface_id, process_id, user_id, attributes);
-  if (!surface_status) {
-    ALOGE("DisplayService::OnCreateSurface: Failed to create surface: %s",
-          surface_status.GetErrorMessage().c_str());
-    return ErrorStatus(surface_status.error());
-  }
-  auto surface = surface_status.take();
-  message.SetChannel(surface);
-
-  // Update the surface with the attributes supplied with the create call. For
-  // application surfaces this has the side effect of notifying the display
-  // manager of the new surface. For direct surfaces, this may trigger a mode
-  // change, depending on the value of the visible attribute.
-  surface->OnSetAttributes(message, attributes);
-
-  return {{surface->surface_id(), surface->visible(), surface->z_order()}};
-}
-
-void DisplayService::SurfaceUpdated(SurfaceType surface_type,
-                                    display::SurfaceUpdateFlags update_flags) {
-  ALOGD_IF(TRACE, "DisplayService::SurfaceUpdated: update_flags=%x",
-           update_flags.value());
-  if (update_flags.value() != 0) {
-    if (surface_type == SurfaceType::Application)
-      NotifyDisplayConfigurationUpdate();
-    else
-      UpdateActiveDisplaySurfaces();
-  }
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnSetupGlobalBuffer(
-    pdx::Message& message, DvrGlobalBufferKey key, size_t size,
-    uint64_t usage) {
-  const int user_id = message.GetEffectiveUserId();
-  const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
-
-  if (!trusted) {
-    ALOGE(
-        "DisplayService::OnSetupGlobalBuffer: Permission denied for user_id=%d",
-        user_id);
-    return ErrorStatus(EPERM);
-  }
-  return SetupGlobalBuffer(key, size, usage);
-}
-
-pdx::Status<void> DisplayService::OnDeleteGlobalBuffer(pdx::Message& message,
-                                                       DvrGlobalBufferKey key) {
-  const int user_id = message.GetEffectiveUserId();
-  const bool trusted = (user_id == AID_ROOT) || IsTrustedUid(user_id);
-
-  if (!trusted) {
-    ALOGE(
-        "DisplayService::OnDeleteGlobalBuffer: Permission denied for "
-        "user_id=%d",
-        user_id);
-    return ErrorStatus(EPERM);
-  }
-  return DeleteGlobalBuffer(key);
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetGlobalBuffer(
-    pdx::Message& /* message */, DvrGlobalBufferKey key) {
-  ALOGD_IF(TRACE, "DisplayService::OnGetGlobalBuffer: key=%d", key);
-  auto global_buffer = global_buffers_.find(key);
-  if (global_buffer != global_buffers_.end())
-    return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
-  else
-    return pdx::ErrorStatus(EINVAL);
-}
-
-// Calls the message handler for the DisplaySurface associated with this
-// channel.
-Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) {
-  auto surface = std::static_pointer_cast<DisplaySurface>(message.GetChannel());
-  ALOGW_IF(!surface,
-           "DisplayService::HandleSurfaceMessage: surface is nullptr!");
-
-  if (surface)
-    return surface->HandleMessage(message);
-  else
-    return ErrorStatus(EINVAL);
-}
-
-std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface(
-    int surface_id) const {
-  return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id));
-}
-
-std::vector<std::shared_ptr<DisplaySurface>>
-DisplayService::GetDisplaySurfaces() const {
-  return GetChannels<DisplaySurface>();
-}
-
-std::vector<std::shared_ptr<DirectDisplaySurface>>
-DisplayService::GetVisibleDisplaySurfaces() const {
-  std::vector<std::shared_ptr<DirectDisplaySurface>> visible_surfaces;
-
-  ForEachDisplaySurface(
-      SurfaceType::Direct,
-      [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
-        if (surface->visible()) {
-          visible_surfaces.push_back(
-              std::static_pointer_cast<DirectDisplaySurface>(surface));
-          surface->ClearUpdate();
-        }
-      });
-
-  return visible_surfaces;
-}
-
-void DisplayService::UpdateActiveDisplaySurfaces() {
-  auto visible_surfaces = GetVisibleDisplaySurfaces();
-  ALOGD_IF(TRACE,
-           "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
-           visible_surfaces.size());
-  hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupGlobalBuffer(
-    DvrGlobalBufferKey key, size_t size, uint64_t usage) {
-  auto global_buffer = global_buffers_.find(key);
-  if (global_buffer == global_buffers_.end()) {
-    auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1,
-                                                  HAL_PIXEL_FORMAT_BLOB, usage);
-
-    // Some buffers are used internally. If they were configured with an
-    // invalid size or format, this will fail.
-    int result = hardware_composer_.OnNewGlobalBuffer(key, *ion_buffer.get());
-    if (result < 0)
-      return ErrorStatus(result);
-    global_buffer =
-        global_buffers_.insert(std::make_pair(key, std::move(ion_buffer)))
-            .first;
-  }
-
-  return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
-}
-
-pdx::Status<void> DisplayService::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
-  auto global_buffer = global_buffers_.find(key);
-  if (global_buffer != global_buffers_.end()) {
-    // Some buffers are used internally.
-    hardware_composer_.OnDeletedGlobalBuffer(key);
-    global_buffers_.erase(global_buffer);
-  }
-
-  return {0};
-}
-
-void DisplayService::SetDisplayConfigurationUpdateNotifier(
-    DisplayConfigurationUpdateNotifier update_notifier) {
-  update_notifier_ = update_notifier;
-}
-
-void DisplayService::NotifyDisplayConfigurationUpdate() {
-  if (update_notifier_)
-    update_notifier_();
-}
-
-Status<bool> DisplayService::IsVrAppRunning(pdx::Message& /*message*/) {
-  bool visible = false;
-  ForEachDisplaySurface(
-      SurfaceType::Application,
-      [&visible](const std::shared_ptr<DisplaySurface>& surface) {
-        if (surface->visible())
-          visible = true;
-      });
-
-  return {visible};
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
deleted file mode 100644
index 89f1eae..0000000
--- a/libs/vr/libvrflinger/display_service.h
+++ /dev/null
@@ -1,126 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
-
-#include <dvr/dvr_api.h>
-#include <pdx/service.h>
-#include <pdx/status.h>
-#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/display_protocol.h>
-
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "acquired_buffer.h"
-#include "display_surface.h"
-#include "epoll_event_dispatcher.h"
-#include "hardware_composer.h"
-
-namespace android {
-namespace dvr {
-
-// DisplayService implements the display service component of VrFlinger.
-class DisplayService : public pdx::ServiceBase<DisplayService> {
- public:
-  bool IsInitialized() const override;
-  std::string DumpState(size_t max_length) override;
-
-  void OnChannelClose(pdx::Message& message,
-                      const std::shared_ptr<pdx::Channel>& channel) override;
-  pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
-  std::shared_ptr<DisplaySurface> GetDisplaySurface(int surface_id) const;
-  std::vector<std::shared_ptr<DisplaySurface>> GetDisplaySurfaces() const;
-  std::vector<std::shared_ptr<DirectDisplaySurface>> GetVisibleDisplaySurfaces()
-      const;
-
-  // Updates the list of actively displayed surfaces. This must be called after
-  // any change to client/manager attributes that affect visibility or z order.
-  void UpdateActiveDisplaySurfaces();
-
-  pdx::Status<BorrowedNativeBufferHandle> SetupGlobalBuffer(
-      DvrGlobalBufferKey key, size_t size, uint64_t usage);
-
-  pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
-
-  template <class A>
-  void ForEachDisplaySurface(SurfaceType surface_type, A action) const {
-    ForEachChannel([surface_type,
-                    action](const ChannelIterator::value_type& pair) mutable {
-      auto surface = std::static_pointer_cast<DisplaySurface>(pair.second);
-      if (surface->surface_type() == surface_type)
-        action(surface);
-    });
-  }
-
-  using DisplayConfigurationUpdateNotifier = std::function<void(void)>;
-  void SetDisplayConfigurationUpdateNotifier(
-      DisplayConfigurationUpdateNotifier notifier);
-
-  void GrantDisplayOwnership() { hardware_composer_.Enable(); }
-  void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
-  void OnBootFinished() { hardware_composer_.OnBootFinished(); }
-
- private:
-  friend BASE;
-  friend DisplaySurface;
-
-  friend class VrDisplayStateService;
-
-  using RequestDisplayCallback = std::function<void(bool)>;
-
-  DisplayService(android::Hwc2::Composer* hidl,
-                 hwc2_display_t primary_display_id,
-                 RequestDisplayCallback request_display_callback);
-
-  pdx::Status<BorrowedNativeBufferHandle> OnGetGlobalBuffer(
-      pdx::Message& message, DvrGlobalBufferKey key);
-  pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message);
-  pdx::Status<std::string> OnGetConfigurationData(
-      pdx::Message& message, display::ConfigFileType config_type);
-  pdx::Status<uint8_t> OnGetDisplayIdentificationPort(pdx::Message& message);
-  pdx::Status<display::SurfaceInfo> OnCreateSurface(
-      pdx::Message& message, const display::SurfaceAttributes& attributes);
-  pdx::Status<BorrowedNativeBufferHandle> OnSetupGlobalBuffer(
-      pdx::Message& message, DvrGlobalBufferKey key, size_t size,
-      uint64_t usage);
-  pdx::Status<void> OnDeleteGlobalBuffer(pdx::Message& message,
-                                         DvrGlobalBufferKey key);
-
-  // Temporary query for current VR status. Will be removed later.
-  pdx::Status<bool> IsVrAppRunning(pdx::Message& message);
-
-  pdx::Status<void> AddEventHandler(int fd, int events,
-                                    EpollEventDispatcher::Handler handler) {
-    return dispatcher_.AddEventHandler(fd, events, handler);
-  }
-  pdx::Status<void> RemoveEventHandler(int fd) {
-    return dispatcher_.RemoveEventHandler(fd);
-  }
-
-  void SurfaceUpdated(SurfaceType surface_type,
-                      display::SurfaceUpdateFlags update_flags);
-
-  // Called by DisplaySurface to signal that a surface property has changed and
-  // the display manager should be notified.
-  void NotifyDisplayConfigurationUpdate();
-
-  pdx::Status<void> HandleSurfaceMessage(pdx::Message& message);
-
-  HardwareComposer hardware_composer_;
-  EpollEventDispatcher dispatcher_;
-  DisplayConfigurationUpdateNotifier update_notifier_;
-
-  std::unordered_map<DvrGlobalBufferKey, std::unique_ptr<IonBuffer>>
-      global_buffers_;
-
-  DisplayService(const DisplayService&) = delete;
-  void operator=(const DisplayService&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
deleted file mode 100644
index 87c823e..0000000
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ /dev/null
@@ -1,488 +0,0 @@
-#include "display_surface.h"
-
-#include <private/android_filesystem_config.h>
-#include <utils/Trace.h>
-
-#include <private/dvr/trusted_uids.h>
-
-#include "display_service.h"
-#include "hardware_composer.h"
-
-#define LOCAL_TRACE 1
-
-using android::dvr::display::DisplayProtocol;
-using android::pdx::BorrowedChannelHandle;
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Message;
-using android::pdx::RemoteChannelHandle;
-using android::pdx::Status;
-using android::pdx::rpc::DispatchRemoteMethod;
-using android::pdx::rpc::IfAnyOf;
-
-namespace android {
-namespace dvr {
-
-DisplaySurface::DisplaySurface(DisplayService* service,
-                               SurfaceType surface_type, int surface_id,
-                               int process_id, int user_id)
-    : service_(service),
-      surface_type_(surface_type),
-      surface_id_(surface_id),
-      process_id_(process_id),
-      user_id_(user_id),
-      update_flags_(display::SurfaceUpdateFlags::NewSurface) {}
-
-DisplaySurface::~DisplaySurface() {
-  ALOGD_IF(LOCAL_TRACE,
-           "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d",
-           surface_id(), process_id());
-}
-
-Status<void> DisplaySurface::HandleMessage(pdx::Message& message) {
-  switch (message.GetOp()) {
-    case DisplayProtocol::SetAttributes::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::SetAttributes>(
-          *this, &DisplaySurface::OnSetAttributes, message);
-      break;
-
-    case DisplayProtocol::GetSurfaceInfo::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::GetSurfaceInfo>(
-          *this, &DisplaySurface::OnGetSurfaceInfo, message);
-      break;
-
-    case DisplayProtocol::CreateQueue::Opcode:
-      DispatchRemoteMethod<DisplayProtocol::CreateQueue>(
-          *this, &DisplaySurface::OnCreateQueue, message);
-      break;
-  }
-
-  return {};
-}
-
-Status<void> DisplaySurface::OnSetAttributes(
-    pdx::Message& /*message*/, const display::SurfaceAttributes& attributes) {
-  display::SurfaceUpdateFlags update_flags;
-
-  for (const auto& attribute : attributes) {
-    const auto key = attribute.first;
-    const auto* variant = &attribute.second;
-    bool invalid_value = false;
-    bool visibility_changed = false;
-
-    // Catch attributes that have significance to the display service.
-    switch (key) {
-      case display::SurfaceAttribute::ZOrder:
-        invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
-            variant, [&](const auto& value) {
-              if (z_order_ != value) {
-                visibility_changed = true;
-                z_order_ = value;
-              }
-            });
-        break;
-      case display::SurfaceAttribute::Visible:
-        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
-            variant, [&](const auto& value) {
-              if (visible_ != value) {
-                visibility_changed = true;
-                visible_ = value;
-              }
-            });
-        break;
-    }
-
-    // Only update the attribute map with valid values. This check also has the
-    // effect of preventing special attributes handled above from being deleted
-    // by an empty value.
-    if (invalid_value) {
-      ALOGW(
-          "DisplaySurface::OnClientSetAttributes: Failed to set display "
-          "surface attribute '%d' because of incompatible type: %d",
-          key, variant->index());
-    } else {
-      // An empty value indicates the attribute should be deleted.
-      if (variant->empty()) {
-        auto search = attributes_.find(key);
-        if (search != attributes_.end())
-          attributes_.erase(search);
-      } else {
-        attributes_[key] = *variant;
-      }
-
-      // All attribute changes generate a notification, even if the value
-      // doesn't change. Visibility attributes set a flag only if the value
-      // changes.
-      update_flags.Set(display::SurfaceUpdateFlags::AttributesChanged);
-      if (visibility_changed)
-        update_flags.Set(display::SurfaceUpdateFlags::VisibilityChanged);
-    }
-  }
-
-  SurfaceUpdated(update_flags);
-  return {};
-}
-
-void DisplaySurface::SurfaceUpdated(display::SurfaceUpdateFlags update_flags) {
-  ALOGD_IF(TRACE,
-           "DisplaySurface::SurfaceUpdated: surface_id=%d update_flags=0x%x",
-           surface_id(), update_flags.value());
-
-  update_flags_.Set(update_flags);
-  service()->SurfaceUpdated(surface_type(), update_flags_);
-}
-
-void DisplaySurface::ClearUpdate() {
-  ALOGD_IF(TRACE > 1, "DisplaySurface::ClearUpdate: surface_id=%d",
-           surface_id());
-  update_flags_ = display::SurfaceUpdateFlags::None;
-}
-
-Status<display::SurfaceInfo> DisplaySurface::OnGetSurfaceInfo(
-    Message& /*message*/) {
-  ALOGD_IF(
-      TRACE,
-      "DisplaySurface::OnGetSurfaceInfo: surface_id=%d visible=%d z_order=%d",
-      surface_id(), visible(), z_order());
-  return {{surface_id(), visible(), z_order()}};
-}
-
-Status<void> DisplaySurface::RegisterQueue(
-    const std::shared_ptr<ConsumerQueue>& consumer_queue) {
-  ALOGD_IF(TRACE, "DisplaySurface::RegisterQueue: surface_id=%d queue_id=%d",
-           surface_id(), consumer_queue->id());
-  // Capture references for the lambda to work around apparent clang bug.
-  // TODO(eieio): Figure out if there is a clang bug or C++11 ambiguity when
-  // capturing self and consumer_queue by copy in the following case:
-  //    auto self = Self();
-  //    [self, consumer_queue](int events) {
-  //        self->OnQueueEvent(consuemr_queue, events); }
-  //
-  struct State {
-    std::shared_ptr<DisplaySurface> surface;
-    std::shared_ptr<ConsumerQueue> queue;
-  };
-  State state{Self(), consumer_queue};
-
-  return service()->AddEventHandler(
-      consumer_queue->queue_fd(), EPOLLIN | EPOLLHUP | EPOLLET,
-      [state](int events) {
-        state.surface->OnQueueEvent(state.queue, events);
-      });
-}
-
-Status<void> DisplaySurface::UnregisterQueue(
-    const std::shared_ptr<ConsumerQueue>& consumer_queue) {
-  ALOGD_IF(TRACE, "DisplaySurface::UnregisterQueue: surface_id=%d queue_id=%d",
-           surface_id(), consumer_queue->id());
-  return service()->RemoveEventHandler(consumer_queue->queue_fd());
-}
-
-void DisplaySurface::OnQueueEvent(
-    const std::shared_ptr<ConsumerQueue>& /*consumer_queue*/, int /*events*/) {
-  ALOGE(
-      "DisplaySurface::OnQueueEvent: ERROR base virtual method should not be "
-      "called!!!");
-}
-
-std::shared_ptr<ConsumerQueue> ApplicationDisplaySurface::GetQueue(
-    int32_t queue_id) {
-  ALOGD_IF(TRACE,
-           "ApplicationDisplaySurface::GetQueue: surface_id=%d queue_id=%d",
-           surface_id(), queue_id);
-
-  std::lock_guard<std::mutex> autolock(lock_);
-  auto search = consumer_queues_.find(queue_id);
-  if (search != consumer_queues_.end())
-    return search->second;
-  else
-    return nullptr;
-}
-
-std::vector<int32_t> ApplicationDisplaySurface::GetQueueIds() const {
-  std::lock_guard<std::mutex> autolock(lock_);
-  std::vector<int32_t> queue_ids;
-  for (const auto& entry : consumer_queues_)
-    queue_ids.push_back(entry.first);
-  return queue_ids;
-}
-
-Status<LocalChannelHandle> ApplicationDisplaySurface::OnCreateQueue(
-    Message& /*message*/, const ProducerQueueConfig& config) {
-  ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue");
-  ALOGD_IF(TRACE,
-           "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, "
-           "user_metadata_size=%zu",
-           surface_id(), config.user_metadata_size);
-
-  std::lock_guard<std::mutex> autolock(lock_);
-  auto producer = ProducerQueue::Create(config, UsagePolicy{});
-  if (!producer) {
-    ALOGE(
-        "ApplicationDisplaySurface::OnCreateQueue: Failed to create producer "
-        "queue!");
-    return ErrorStatus(ENOMEM);
-  }
-
-  std::shared_ptr<ConsumerQueue> consumer =
-      producer->CreateSilentConsumerQueue();
-  auto status = RegisterQueue(consumer);
-  if (!status) {
-    ALOGE(
-        "ApplicationDisplaySurface::OnCreateQueue: Failed to register consumer "
-        "queue: %s",
-        status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  consumer_queues_[consumer->id()] = std::move(consumer);
-
-  SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
-  return std::move(producer->GetChannelHandle());
-}
-
-void ApplicationDisplaySurface::OnQueueEvent(
-    const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
-  ALOGD_IF(TRACE,
-           "ApplicationDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
-           consumer_queue->id(), events);
-
-  std::lock_guard<std::mutex> autolock(lock_);
-
-  // Always give the queue a chance to handle its internal bookkeeping.
-  consumer_queue->HandleQueueEvents();
-
-  // Check for hangup and remove a queue that is no longer needed.
-  if (consumer_queue->hung_up()) {
-    ALOGD_IF(TRACE, "ApplicationDisplaySurface::OnQueueEvent: Removing queue.");
-    UnregisterQueue(consumer_queue);
-    auto search = consumer_queues_.find(consumer_queue->id());
-    if (search != consumer_queues_.end()) {
-      consumer_queues_.erase(search);
-    } else {
-      ALOGE(
-          "ApplicationDisplaySurface::OnQueueEvent: Failed to find queue_id=%d",
-          consumer_queue->id());
-    }
-    SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
-  }
-}
-
-std::vector<int32_t> DirectDisplaySurface::GetQueueIds() const {
-  std::lock_guard<std::mutex> autolock(lock_);
-  std::vector<int32_t> queue_ids;
-  if (direct_queue_)
-    queue_ids.push_back(direct_queue_->id());
-  return queue_ids;
-}
-
-Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue(
-    Message& /*message*/, const ProducerQueueConfig& config) {
-  ATRACE_NAME("DirectDisplaySurface::OnCreateQueue");
-  ALOGD_IF(TRACE,
-           "DirectDisplaySurface::OnCreateQueue: surface_id=%d "
-           "user_metadata_size=%zu",
-           surface_id(), config.user_metadata_size);
-
-  std::lock_guard<std::mutex> autolock(lock_);
-  if (!direct_queue_) {
-    // Inject the hw composer usage flag to enable the display to read the
-    // buffers.
-    auto producer = ProducerQueue::Create(
-        config, UsagePolicy{GraphicBuffer::USAGE_HW_COMPOSER, 0, 0, 0});
-    if (!producer) {
-      ALOGE(
-          "DirectDisplaySurface::OnCreateQueue: Failed to create producer "
-          "queue!");
-      return ErrorStatus(ENOMEM);
-    }
-
-    direct_queue_ = producer->CreateConsumerQueue();
-    if (direct_queue_->metadata_size() > 0) {
-      metadata_.reset(new uint8_t[direct_queue_->metadata_size()]);
-    }
-    auto status = RegisterQueue(direct_queue_);
-    if (!status) {
-      ALOGE(
-          "DirectDisplaySurface::OnCreateQueue: Failed to register consumer "
-          "queue: %s",
-          status.GetErrorMessage().c_str());
-      return status.error_status();
-    }
-
-    return std::move(producer->GetChannelHandle());
-  } else {
-    return ErrorStatus(EALREADY);
-  }
-}
-
-void DirectDisplaySurface::OnQueueEvent(
-    const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
-  ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
-           consumer_queue->id(), events);
-
-  std::lock_guard<std::mutex> autolock(lock_);
-
-  // Always give the queue a chance to handle its internal bookkeeping.
-  consumer_queue->HandleQueueEvents();
-
-  // Check for hangup and remove a queue that is no longer needed.
-  if (consumer_queue->hung_up()) {
-    ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: Removing queue.");
-    UnregisterQueue(consumer_queue);
-    direct_queue_ = nullptr;
-  }
-}
-
-void DirectDisplaySurface::DequeueBuffersLocked() {
-  if (direct_queue_ == nullptr) {
-    ALOGE(
-        "DirectDisplaySurface::DequeueBuffersLocked: Consumer queue is not "
-        "initialized.");
-    return;
-  }
-
-  while (true) {
-    LocalHandle acquire_fence;
-    size_t slot;
-    auto buffer_status = direct_queue_->Dequeue(
-        0, &slot, metadata_.get(),
-        direct_queue_->metadata_size(), &acquire_fence);
-    ALOGD_IF(TRACE,
-             "DirectDisplaySurface::DequeueBuffersLocked: Dequeue with metadata_size: %zu",
-             direct_queue_->metadata_size());
-    if (!buffer_status) {
-      ALOGD_IF(
-          TRACE > 1 && buffer_status.error() == ETIMEDOUT,
-          "DirectDisplaySurface::DequeueBuffersLocked: All buffers dequeued.");
-      ALOGE_IF(buffer_status.error() != ETIMEDOUT,
-               "DirectDisplaySurface::DequeueBuffersLocked: Failed to dequeue "
-               "buffer: %s",
-               buffer_status.GetErrorMessage().c_str());
-      return;
-    }
-    auto buffer_consumer = buffer_status.take();
-
-    if (!visible()) {
-      ATRACE_NAME("DropFrameOnInvisibleSurface");
-      ALOGD_IF(TRACE,
-               "DirectDisplaySurface::DequeueBuffersLocked: Discarding "
-               "buffer_id=%d on invisible surface.",
-               buffer_consumer->id());
-      buffer_consumer->Discard();
-      continue;
-    }
-
-    if (acquired_buffers_.IsFull()) {
-      ALOGE(
-          "DirectDisplaySurface::DequeueBuffersLocked: Posted buffers full, "
-          "overwriting.");
-      acquired_buffers_.PopBack();
-    }
-
-    acquired_buffers_.Append(
-        AcquiredBuffer(buffer_consumer, std::move(acquire_fence), slot));
-  }
-}
-
-AcquiredBuffer DirectDisplaySurface::AcquireCurrentBuffer() {
-  std::lock_guard<std::mutex> autolock(lock_);
-  DequeueBuffersLocked();
-
-  if (acquired_buffers_.IsEmpty()) {
-    ALOGE(
-        "DirectDisplaySurface::AcquireCurrentBuffer: attempt to acquire buffer "
-        "when none are posted.");
-    return AcquiredBuffer();
-  }
-  AcquiredBuffer buffer = std::move(acquired_buffers_.Front());
-  acquired_buffers_.PopFront();
-  ALOGD_IF(TRACE, "DirectDisplaySurface::AcquireCurrentBuffer: buffer_id=%d",
-           buffer.buffer()->id());
-  return buffer;
-}
-
-AcquiredBuffer DirectDisplaySurface::AcquireNewestAvailableBuffer(
-    AcquiredBuffer* skipped_buffer) {
-  std::lock_guard<std::mutex> autolock(lock_);
-  DequeueBuffersLocked();
-
-  AcquiredBuffer buffer;
-  int frames = 0;
-  // Basic latency stopgap for when the application misses a frame:
-  // If the application recovers on the 2nd or 3rd (etc) frame after
-  // missing, this code will skip frames to catch up by checking if
-  // the next frame is also available.
-  while (!acquired_buffers_.IsEmpty() &&
-         acquired_buffers_.Front().IsAvailable()) {
-    // Capture the skipped buffer into the result parameter.
-    // Note that this API only supports skipping one buffer per vsync.
-    if (frames > 0 && skipped_buffer)
-      *skipped_buffer = std::move(buffer);
-    ++frames;
-    buffer = std::move(acquired_buffers_.Front());
-    acquired_buffers_.PopFront();
-    if (frames == 2)
-      break;
-  }
-  ALOGD_IF(TRACE,
-           "DirectDisplaySurface::AcquireNewestAvailableBuffer: buffer_id=%d",
-           buffer.buffer()->id());
-  return buffer;
-}
-
-bool DirectDisplaySurface::IsBufferAvailable() {
-  std::lock_guard<std::mutex> autolock(lock_);
-  DequeueBuffersLocked();
-
-  return !acquired_buffers_.IsEmpty() &&
-         acquired_buffers_.Front().IsAvailable();
-}
-
-bool DirectDisplaySurface::IsBufferPosted() {
-  std::lock_guard<std::mutex> autolock(lock_);
-  DequeueBuffersLocked();
-
-  return !acquired_buffers_.IsEmpty();
-}
-
-Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create(
-    DisplayService* service, int surface_id, int process_id, int user_id,
-    const display::SurfaceAttributes& attributes) {
-  bool direct = false;
-  auto search = attributes.find(display::SurfaceAttribute::Direct);
-  if (search != attributes.end()) {
-    if (!IfAnyOf<int32_t, int64_t, bool, float>::Get(&search->second,
-                                                     &direct)) {
-      ALOGE(
-          "DisplaySurface::Create: Invalid type for SurfaceAttribute::Direct!");
-      return ErrorStatus(EINVAL);
-    }
-  }
-
-  ALOGD_IF(TRACE,
-           "DisplaySurface::Create: surface_id=%d process_id=%d user_id=%d "
-           "direct=%d",
-           surface_id, process_id, user_id, direct);
-
-  if (direct) {
-    const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
-    if (trusted) {
-      return {std::shared_ptr<DisplaySurface>{
-          new DirectDisplaySurface(service, surface_id, process_id, user_id)}};
-    } else {
-      ALOGE(
-          "DisplaySurface::Create: Direct surfaces may only be created by "
-          "trusted UIDs: user_id=%d",
-          user_id);
-      return ErrorStatus(EPERM);
-    }
-  } else {
-    return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface(
-        service, surface_id, process_id, user_id)}};
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
deleted file mode 100644
index c8b1a07..0000000
--- a/libs/vr/libvrflinger/display_surface.h
+++ /dev/null
@@ -1,188 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
-
-#include <pdx/file_handle.h>
-#include <pdx/service.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/ring_buffer.h>
-
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "acquired_buffer.h"
-
-namespace android {
-namespace dvr {
-
-class DisplayService;
-
-enum class SurfaceType {
-  Direct,
-  Application,
-};
-
-class DisplaySurface : public pdx::Channel {
- public:
-  static pdx::Status<std::shared_ptr<DisplaySurface>> Create(
-      DisplayService* service, int surface_id, int process_id, int user_id,
-      const display::SurfaceAttributes& attributes);
-
-  ~DisplaySurface() override;
-
-  DisplayService* service() const { return service_; }
-  SurfaceType surface_type() const { return surface_type_; }
-  int surface_id() const { return surface_id_; }
-  int process_id() const { return process_id_; }
-  int user_id() const { return user_id_; }
-
-  bool visible() const { return visible_; }
-  int z_order() const { return z_order_; }
-
-  const display::SurfaceAttributes& attributes() const { return attributes_; }
-  display::SurfaceUpdateFlags update_flags() const { return update_flags_; }
-
-  virtual std::vector<int32_t> GetQueueIds() const { return {}; }
-
-  bool IsUpdatePending() const {
-    return update_flags_.value() != display::SurfaceUpdateFlags::None;
-  }
-
- protected:
-  DisplaySurface(DisplayService* service, SurfaceType surface_type,
-                 int surface_id, int process_id, int user_id);
-
-  // Utility to retrieve a shared pointer to this channel as the desired derived
-  // type.
-  template <
-      typename T = DisplaySurface,
-      typename = std::enable_if_t<std::is_base_of<DisplaySurface, T>::value>>
-  std::shared_ptr<T> Self() {
-    return std::static_pointer_cast<T>(shared_from_this());
-  }
-
-  virtual pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
-      pdx::Message& message, const ProducerQueueConfig& config) = 0;
-
-  // Registers a consumer queue with the event dispatcher in DisplayService. The
-  // OnQueueEvent callback below is called to handle queue events.
-  pdx::Status<void> RegisterQueue(
-      const std::shared_ptr<ConsumerQueue>& consumer_queue);
-  pdx::Status<void> UnregisterQueue(
-      const std::shared_ptr<ConsumerQueue>& consumer_queue);
-
-  // Called by the event dispatcher in DisplayService when a registered queue
-  // event triggers. Executes on the event dispatcher thread.
-  virtual void OnQueueEvent(
-      const std::shared_ptr<ConsumerQueue>& consumer_queue, int events);
-
-  void SurfaceUpdated(display::SurfaceUpdateFlags update_flags);
-  void ClearUpdate();
-
-  // Synchronizes access to mutable state below between message dispatch thread
-  // and frame post thread.
-  mutable std::mutex lock_;
-
- private:
-  friend class DisplayService;
-  friend class DisplayManagerService;
-
-  // Dispatches display surface messages to the appropriate handlers. This
-  // handler runs on the VrFlinger message dispatch thread.
-  pdx::Status<void> HandleMessage(pdx::Message& message);
-
-  pdx::Status<void> OnSetAttributes(
-      pdx::Message& message, const display::SurfaceAttributes& attributes);
-  pdx::Status<display::SurfaceInfo> OnGetSurfaceInfo(pdx::Message& message);
-
-  DisplayService* service_;
-  SurfaceType surface_type_;
-  int surface_id_;
-  int process_id_;
-  int user_id_;
-
-  display::SurfaceAttributes attributes_;
-  display::SurfaceUpdateFlags update_flags_ = display::SurfaceUpdateFlags::None;
-
-  // Subset of attributes that may be interpreted by the display service.
-  bool visible_ = false;
-  int z_order_ = 0;
-
-  DisplaySurface(const DisplaySurface&) = delete;
-  void operator=(const DisplaySurface&) = delete;
-};
-
-class ApplicationDisplaySurface : public DisplaySurface {
- public:
-  ApplicationDisplaySurface(DisplayService* service, int surface_id,
-                            int process_id, int user_id)
-      : DisplaySurface(service, SurfaceType::Application, surface_id,
-                       process_id, user_id) {}
-
-  std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id);
-  std::vector<int32_t> GetQueueIds() const override;
-
- private:
-  pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
-      pdx::Message& message, const ProducerQueueConfig& config) override;
-  void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
-                    int events) override;
-
-  // Accessed by both message dispatch thread and epoll event thread.
-  std::unordered_map<int32_t, std::shared_ptr<ConsumerQueue>> consumer_queues_;
-};
-
-class DirectDisplaySurface : public DisplaySurface {
- public:
-  DirectDisplaySurface(DisplayService* service, int surface_id, int process_id,
-                       int user_id)
-      : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id,
-                       user_id),
-        acquired_buffers_(kMaxPostedBuffers),
-        metadata_(nullptr) {}
-  std::vector<int32_t> GetQueueIds() const override;
-  bool IsBufferAvailable();
-  bool IsBufferPosted();
-  AcquiredBuffer AcquireCurrentBuffer();
-
-  // Get the newest buffer. Up to one buffer will be skipped. If a buffer is
-  // skipped, it will be stored in skipped_buffer if non null.
-  AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer);
-
- private:
-  pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
-      pdx::Message& message, const ProducerQueueConfig& config) override;
-  void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
-                    int events) override;
-
-  // The capacity of the pending buffer queue. Should be enough to hold all the
-  // buffers of this DisplaySurface, although in practice only 1 or 2 frames
-  // will be pending at a time.
-  static constexpr int kSurfaceBufferMaxCount = 4;
-  static constexpr int kSurfaceViewMaxCount = 4;
-  static constexpr int kMaxPostedBuffers =
-      kSurfaceBufferMaxCount * kSurfaceViewMaxCount;
-
-  // Returns whether a frame is available without locking the mutex.
-  bool IsFrameAvailableNoLock() const;
-
-  // Dequeue all available buffers from the consumer queue.
-  void DequeueBuffersLocked();
-
-  // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
-  // posted and pending.
-  RingBuffer<AcquiredBuffer> acquired_buffers_;
-
-  std::shared_ptr<ConsumerQueue> direct_queue_;
-
-  // Stores metadata when it dequeue buffers from consumer queue.
-  std::unique_ptr<uint8_t[]> metadata_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
deleted file mode 100644
index 0d5eb80..0000000
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "epoll_event_dispatcher.h"
-
-#include <log/log.h>
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-#include <sys/prctl.h>
-
-#include <dvr/performance_client_api.h>
-
-namespace android {
-namespace dvr {
-
-EpollEventDispatcher::EpollEventDispatcher() {
-  epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
-  if (!epoll_fd_) {
-    ALOGE("Failed to create epoll fd: %s", strerror(errno));
-    return;
-  }
-
-  event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-  if (!event_fd_) {
-    ALOGE("Failed to create event for epolling: %s", strerror(errno));
-    return;
-  }
-
-  // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
-  // when eventfd_write occurs. Use "this" as a unique sentinal value to
-  // identify events from the event fd.
-  epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
-    ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
-    return;
-  }
-
-  thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
-}
-
-EpollEventDispatcher::~EpollEventDispatcher() { Stop(); }
-
-void EpollEventDispatcher::Stop() {
-  exit_thread_.store(true);
-  eventfd_write(event_fd_.Get(), 1);
-}
-
-pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
-                                                        Handler handler) {
-  std::lock_guard<std::mutex> lock(lock_);
-
-  epoll_event event;
-  event.events = event_mask;
-  event.data.ptr = &(handlers_[fd] = handler);
-
-  ALOGD_IF(
-      TRACE,
-      "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
-      fd, event_mask, event.data.ptr);
-
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) {
-    const int error = errno;
-    ALOGE("Failed to add fd to epoll set because: %s", strerror(error));
-    return pdx::ErrorStatus(error);
-  } else {
-    return {};
-  }
-}
-
-pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) {
-  ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
-  std::lock_guard<std::mutex> lock(lock_);
-
-  epoll_event ee;  // See BUGS in man 2 epoll_ctl.
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) {
-    const int error = errno;
-    ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
-    return pdx::ErrorStatus(error);
-  }
-
-  // If the fd was valid above, add it to the list of ids to remove.
-  removed_handlers_.push_back(fd);
-
-  // Wake up the event thread to clean up.
-  eventfd_write(event_fd_.Get(), 1);
-
-  return {};
-}
-
-void EpollEventDispatcher::EventThread() {
-  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0);
-
-  const int error = dvrSetSchedulerClass(0, "graphics");
-  LOG_ALWAYS_FATAL_IF(
-      error < 0,
-      "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
-      strerror(-error));
-
-  const size_t kMaxNumEvents = 128;
-  epoll_event events[kMaxNumEvents];
-
-  while (!exit_thread_.load()) {
-    const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1);
-    if (num_events < 0 && errno != EINTR)
-      break;
-
-    ALOGD_IF(TRACE > 1, "EpollEventDispatcher::EventThread: num_events=%d",
-             num_events);
-
-    for (int i = 0; i < num_events; i++) {
-      ALOGD_IF(
-          TRACE > 1,
-          "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
-          i, events[i].data.ptr, events[i].events);
-
-      if (events[i].data.ptr == this) {
-        // Clear pending event on event_fd_. Serialize the read with respect to
-        // writes from other threads.
-        std::lock_guard<std::mutex> lock(lock_);
-        eventfd_t value;
-        eventfd_read(event_fd_.Get(), &value);
-      } else {
-        auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
-        if (handler)
-          (*handler)(events[i].events);
-      }
-    }
-
-    // Remove any handlers that have been posted for removal. This is done here
-    // instead of in RemoveEventHandler() to prevent races between the dispatch
-    // thread and the code requesting the removal. Handlers are guaranteed to
-    // stay alive between exiting epoll_wait() and the dispatch loop above.
-    std::lock_guard<std::mutex> lock(lock_);
-    for (auto handler_fd : removed_handlers_) {
-      ALOGD_IF(TRACE,
-               "EpollEventDispatcher::EventThread: removing handler: fd=%d",
-               handler_fd);
-      handlers_.erase(handler_fd);
-    }
-    removed_handlers_.clear();
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.h b/libs/vr/libvrflinger/epoll_event_dispatcher.h
deleted file mode 100644
index eb687f4..0000000
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
-
-#include <sys/epoll.h>
-
-#include <atomic>
-#include <functional>
-#include <mutex>
-#include <thread>
-#include <unordered_map>
-#include <vector>
-
-#include <pdx/file_handle.h>
-#include <pdx/status.h>
-
-namespace android {
-namespace dvr {
-
-class EpollEventDispatcher {
- public:
-  // Function type for event handlers. The handler receives a bitmask of the
-  // epoll events that occurred on the file descriptor associated with the
-  // handler.
-  using Handler = std::function<void(int)>;
-
-  EpollEventDispatcher();
-  ~EpollEventDispatcher();
-
-  // |handler| is called on the internal dispatch thread when |fd| is signaled
-  // by events in |event_mask|.
-  pdx::Status<void> AddEventHandler(int fd, int event_mask, Handler handler);
-  pdx::Status<void> RemoveEventHandler(int fd);
-
-  void Stop();
-
- private:
-  void EventThread();
-
-  std::thread thread_;
-  std::atomic<bool> exit_thread_{false};
-
-  // Protects handlers_ and removed_handlers_ and serializes operations on
-  // epoll_fd_ and event_fd_.
-  std::mutex lock_;
-
-  // Maintains a map of fds to event handlers. This is primarily to keep any
-  // references alive that may be bound in the std::function instances. It is
-  // not used at dispatch time to avoid performance problems with different
-  // versions of std::unordered_map.
-  std::unordered_map<int, Handler> handlers_;
-
-  // List of fds to be removed from the map. The actual removal is performed
-  // by the event dispatch thread to avoid races.
-  std::vector<int> removed_handlers_;
-
-  pdx::LocalHandle epoll_fd_;
-  pdx::LocalHandle event_fd_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
deleted file mode 100644
index 70f303b..0000000
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ /dev/null
@@ -1,1541 +0,0 @@
-#include "hardware_composer.h"
-
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <cutils/sched_policy.h>
-#include <fcntl.h>
-#include <log/log.h>
-#include <poll.h>
-#include <stdint.h>
-#include <sync/sync.h>
-#include <sys/eventfd.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
-#include <sys/system_properties.h>
-#include <sys/timerfd.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include <utils/Trace.h>
-
-#include <algorithm>
-#include <chrono>
-#include <functional>
-#include <map>
-#include <sstream>
-#include <string>
-#include <tuple>
-
-#include <dvr/dvr_display_types.h>
-#include <dvr/performance_client_api.h>
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/ion_buffer.h>
-
-using android::hardware::Return;
-using android::hardware::Void;
-using android::pdx::ErrorStatus;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-using android::pdx::rpc::EmptyVariant;
-using android::pdx::rpc::IfAnyOf;
-
-using namespace std::chrono_literals;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-const char kDvrPerformanceProperty[] = "sys.dvr.performance";
-
-const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
-
-// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these
-// events. Name ours similarly.
-const char kVsyncTraceEventName[] = "VSYNC-vrflinger";
-
-// How long to wait after boot finishes before we turn the display off.
-constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
-
-constexpr int kDefaultDisplayWidth = 1920;
-constexpr int kDefaultDisplayHeight = 1080;
-constexpr int64_t kDefaultVsyncPeriodNs = 16666667;
-// Hardware composer reports dpi as dots per thousand inches (dpi * 1000).
-constexpr int kDefaultDpi = 400000;
-
-// Get time offset from a vsync to when the pose for that vsync should be
-// predicted out to. For example, if scanout gets halfway through the frame
-// at the halfway point between vsyncs, then this could be half the period.
-// With global shutter displays, this should be changed to the offset to when
-// illumination begins. Low persistence adds a frame of latency, so we predict
-// to the center of the next frame.
-inline int64_t GetPosePredictionTimeOffset(int64_t vsync_period_ns) {
-  return (vsync_period_ns * 150) / 100;
-}
-
-// Attempts to set the scheduler class and partiton for the current thread.
-// Returns true on success or false on failure.
-bool SetThreadPolicy(const std::string& scheduler_class,
-                     const std::string& partition) {
-  int error = dvrSetSchedulerClass(0, scheduler_class.c_str());
-  if (error < 0) {
-    ALOGE(
-        "SetThreadPolicy: Failed to set scheduler class \"%s\" for "
-        "thread_id=%d: %s",
-        scheduler_class.c_str(), gettid(), strerror(-error));
-    return false;
-  }
-  error = dvrSetCpuPartition(0, partition.c_str());
-  if (error < 0) {
-    ALOGE(
-        "SetThreadPolicy: Failed to set cpu partiton \"%s\" for thread_id=%d: "
-        "%s",
-        partition.c_str(), gettid(), strerror(-error));
-    return false;
-  }
-  return true;
-}
-
-// Utility to generate scoped tracers with arguments.
-// TODO(eieio): Move/merge this into utils/Trace.h?
-class TraceArgs {
- public:
-  template <typename... Args>
-  explicit TraceArgs(const char* format, Args&&... args) {
-    std::array<char, 1024> buffer;
-    snprintf(buffer.data(), buffer.size(), format, std::forward<Args>(args)...);
-    atrace_begin(ATRACE_TAG, buffer.data());
-  }
-
-  ~TraceArgs() { atrace_end(ATRACE_TAG); }
-
- private:
-  TraceArgs(const TraceArgs&) = delete;
-  void operator=(const TraceArgs&) = delete;
-};
-
-// Macro to define a scoped tracer with arguments. Uses PASTE(x, y) macro
-// defined in utils/Trace.h.
-#define TRACE_FORMAT(format, ...) \
-  TraceArgs PASTE(__tracer, __LINE__) { format, ##__VA_ARGS__ }
-
-// Returns "primary" or "external". Useful for writing more readable logs.
-const char* GetDisplayName(bool is_primary) {
-  return is_primary ? "primary" : "external";
-}
-
-}  // anonymous namespace
-
-HardwareComposer::HardwareComposer()
-    : initialized_(false), request_display_callback_(nullptr) {}
-
-HardwareComposer::~HardwareComposer(void) {
-  UpdatePostThreadState(PostThreadState::Quit, true);
-  if (post_thread_.joinable())
-    post_thread_.join();
-  composer_callback_->SetVsyncService(nullptr);
-}
-
-void HardwareComposer::UpdateEdidData(Hwc2::Composer* composer,
-                                      hwc2_display_t hw_id) {
-  const auto error = composer->getDisplayIdentificationData(
-      hw_id, &display_port_, &display_identification_data_);
-  if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
-    if (error !=
-        android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
-      ALOGI("hardware_composer: identification data error\n");
-    } else {
-      ALOGI("hardware_composer: identification data unsupported\n");
-    }
-  }
-}
-
-bool HardwareComposer::Initialize(
-    Hwc2::Composer* composer, hwc2_display_t primary_display_id,
-    RequestDisplayCallback request_display_callback) {
-  if (initialized_) {
-    ALOGE("HardwareComposer::Initialize: already initialized.");
-    return false;
-  }
-
-  request_display_callback_ = request_display_callback;
-
-  primary_display_ = GetDisplayParams(composer, primary_display_id, true);
-
-  vsync_service_ = new VsyncService;
-  sp<IServiceManager> sm(defaultServiceManager());
-  auto result = sm->addService(String16(VsyncService::GetServiceName()),
-      vsync_service_, false);
-  LOG_ALWAYS_FATAL_IF(result != android::OK,
-      "addService(%s) failed", VsyncService::GetServiceName());
-
-  post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-  LOG_ALWAYS_FATAL_IF(
-      !post_thread_event_fd_,
-      "HardwareComposer: Failed to create interrupt event fd : %s",
-      strerror(errno));
-
-  UpdateEdidData(composer, primary_display_id);
-
-  post_thread_ = std::thread(&HardwareComposer::PostThread, this);
-
-  initialized_ = true;
-
-  return initialized_;
-}
-
-void HardwareComposer::Enable() {
-  UpdatePostThreadState(PostThreadState::Suspended, false);
-}
-
-void HardwareComposer::Disable() {
-  UpdatePostThreadState(PostThreadState::Suspended, true);
-
-  std::unique_lock<std::mutex> lock(post_thread_mutex_);
-  post_thread_ready_.wait(lock, [this] {
-    return !post_thread_resumed_;
-  });
-}
-
-void HardwareComposer::OnBootFinished() {
-  std::lock_guard<std::mutex> lock(post_thread_mutex_);
-  if (boot_finished_)
-    return;
-  boot_finished_ = true;
-  post_thread_wait_.notify_one();
-}
-
-// Update the post thread quiescent state based on idle and suspended inputs.
-void HardwareComposer::UpdatePostThreadState(PostThreadStateType state,
-                                             bool suspend) {
-  std::unique_lock<std::mutex> lock(post_thread_mutex_);
-
-  // Update the votes in the state variable before evaluating the effective
-  // quiescent state. Any bits set in post_thread_state_ indicate that the post
-  // thread should be suspended.
-  if (suspend) {
-    post_thread_state_ |= state;
-  } else {
-    post_thread_state_ &= ~state;
-  }
-
-  const bool quit = post_thread_state_ & PostThreadState::Quit;
-  const bool effective_suspend = post_thread_state_ != PostThreadState::Active;
-  if (quit) {
-    post_thread_quiescent_ = true;
-    eventfd_write(post_thread_event_fd_.Get(), 1);
-    post_thread_wait_.notify_one();
-  } else if (effective_suspend && !post_thread_quiescent_) {
-    post_thread_quiescent_ = true;
-    eventfd_write(post_thread_event_fd_.Get(), 1);
-  } else if (!effective_suspend && post_thread_quiescent_) {
-    post_thread_quiescent_ = false;
-    eventfd_t value;
-    eventfd_read(post_thread_event_fd_.Get(), &value);
-    post_thread_wait_.notify_one();
-  }
-}
-
-void HardwareComposer::CreateComposer() {
-  if (composer_)
-    return;
-  composer_.reset(new Hwc2::impl::Composer("default"));
-  composer_callback_ = new ComposerCallback;
-  composer_->registerCallback(composer_callback_);
-  LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(),
-      "Registered composer callback but didn't get hotplug for primary"
-      " display");
-  composer_callback_->SetVsyncService(vsync_service_);
-}
-
-void HardwareComposer::OnPostThreadResumed() {
-  ALOGI("OnPostThreadResumed");
-  EnableDisplay(*target_display_, true);
-
-  // Trigger target-specific performance mode change.
-  property_set(kDvrPerformanceProperty, "performance");
-}
-
-void HardwareComposer::OnPostThreadPaused() {
-  ALOGI("OnPostThreadPaused");
-  retire_fence_fds_.clear();
-  layers_.clear();
-
-  // Phones create a new composer client on resume and destroy it on pause.
-  if (composer_callback_ != nullptr) {
-    composer_callback_->SetVsyncService(nullptr);
-    composer_callback_ = nullptr;
-  }
-  composer_.reset(nullptr);
-
-  // Trigger target-specific performance mode change.
-  property_set(kDvrPerformanceProperty, "idle");
-}
-
-bool HardwareComposer::PostThreadCondWait(std::unique_lock<std::mutex>& lock,
-                                          int timeout_sec,
-                                          const std::function<bool()>& pred) {
-  auto pred_with_quit = [&] {
-    return pred() || (post_thread_state_ & PostThreadState::Quit);
-  };
-  if (timeout_sec >= 0) {
-    post_thread_wait_.wait_for(lock, std::chrono::seconds(timeout_sec),
-                               pred_with_quit);
-  } else {
-    post_thread_wait_.wait(lock, pred_with_quit);
-  }
-  if (post_thread_state_ & PostThreadState::Quit) {
-    ALOGI("HardwareComposer::PostThread: Quitting.");
-    return true;
-  }
-  return false;
-}
-
-HWC::Error HardwareComposer::Validate(hwc2_display_t display) {
-  uint32_t num_types;
-  uint32_t num_requests;
-  HWC::Error error =
-      composer_->validateDisplay(display, &num_types, &num_requests);
-
-  if (error == HWC2_ERROR_HAS_CHANGES) {
-    ALOGE("Hardware composer has requested composition changes, "
-          "which we don't support.");
-    // Accept the changes anyway and see if we can get something on the screen.
-    error = composer_->acceptDisplayChanges(display);
-  }
-
-  return error;
-}
-
-bool HardwareComposer::EnableVsync(const DisplayParams& display, bool enabled) {
-  HWC::Error error = composer_->setVsyncEnabled(display.id,
-      (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
-                                             : HWC2_VSYNC_DISABLE));
-  if (error != HWC::Error::None) {
-    ALOGE("Error attempting to %s vsync on %s display: %s",
-        enabled ? "enable" : "disable", GetDisplayName(display.is_primary),
-        error.to_string().c_str());
-  }
-  return error == HWC::Error::None;
-}
-
-bool HardwareComposer::SetPowerMode(const DisplayParams& display, bool active) {
-  ALOGI("Turning %s display %s", GetDisplayName(display.is_primary),
-      active ? "on" : "off");
-  HWC::PowerMode power_mode = active ? HWC::PowerMode::On : HWC::PowerMode::Off;
-  HWC::Error error = composer_->setPowerMode(display.id,
-      power_mode.cast<Hwc2::IComposerClient::PowerMode>());
-  if (error != HWC::Error::None) {
-    ALOGE("Error attempting to turn %s display %s: %s",
-          GetDisplayName(display.is_primary), active ? "on" : "off",
-        error.to_string().c_str());
-  }
-  return error == HWC::Error::None;
-}
-
-bool HardwareComposer::EnableDisplay(const DisplayParams& display,
-                                     bool enabled) {
-  bool power_result;
-  bool vsync_result;
-  // When turning a display on, we set the power state then set vsync. When
-  // turning a display off we do it in the opposite order.
-  if (enabled) {
-    power_result = SetPowerMode(display, enabled);
-    vsync_result = EnableVsync(display, enabled);
-  } else {
-    vsync_result = EnableVsync(display, enabled);
-    power_result = SetPowerMode(display, enabled);
-  }
-  return power_result && vsync_result;
-}
-
-HWC::Error HardwareComposer::Present(hwc2_display_t display) {
-  int32_t present_fence;
-  HWC::Error error = composer_->presentDisplay(display, &present_fence);
-
-  // According to the documentation, this fence is signaled at the time of
-  // vsync/DMA for physical displays.
-  if (error == HWC::Error::None) {
-    retire_fence_fds_.emplace_back(present_fence);
-  } else {
-    ATRACE_INT("HardwareComposer: PresentResult", error);
-  }
-
-  return error;
-}
-
-DisplayParams HardwareComposer::GetDisplayParams(
-    Hwc2::Composer* composer, hwc2_display_t display, bool is_primary) {
-  DisplayParams params;
-  params.id = display;
-  params.is_primary = is_primary;
-
-  Hwc2::Config config;
-  HWC::Error error = composer->getActiveConfig(display, &config);
-
-  if (error == HWC::Error::None) {
-    auto get_attr = [&](hwc2_attribute_t attr, const char* attr_name)
-        -> std::optional<int32_t> {
-      int32_t val;
-      HWC::Error error = composer->getDisplayAttribute(
-          display, config, (Hwc2::IComposerClient::Attribute)attr, &val);
-      if (error != HWC::Error::None) {
-        ALOGE("Failed to get %s display attr %s: %s",
-            GetDisplayName(is_primary), attr_name,
-            error.to_string().c_str());
-        return std::nullopt;
-      }
-      return val;
-    };
-
-    auto width = get_attr(HWC2_ATTRIBUTE_WIDTH, "width");
-    auto height = get_attr(HWC2_ATTRIBUTE_HEIGHT, "height");
-
-    if (width && height) {
-      params.width = *width;
-      params.height = *height;
-    } else {
-      ALOGI("Failed to get width and/or height for %s display. Using default"
-          " size %dx%d.", GetDisplayName(is_primary), kDefaultDisplayWidth,
-          kDefaultDisplayHeight);
-      params.width = kDefaultDisplayWidth;
-      params.height = kDefaultDisplayHeight;
-    }
-
-    auto vsync_period = get_attr(HWC2_ATTRIBUTE_VSYNC_PERIOD, "vsync period");
-    if (vsync_period) {
-      params.vsync_period_ns = *vsync_period;
-    } else {
-      ALOGI("Failed to get vsync period for %s display. Using default vsync"
-          " period %.2fms", GetDisplayName(is_primary),
-          static_cast<float>(kDefaultVsyncPeriodNs) / 1000000);
-      params.vsync_period_ns = kDefaultVsyncPeriodNs;
-    }
-
-    auto dpi_x = get_attr(HWC2_ATTRIBUTE_DPI_X, "DPI X");
-    auto dpi_y = get_attr(HWC2_ATTRIBUTE_DPI_Y, "DPI Y");
-    if (dpi_x && dpi_y) {
-      params.dpi.x = *dpi_x;
-      params.dpi.y = *dpi_y;
-    } else {
-      ALOGI("Failed to get dpi_x and/or dpi_y for %s display. Using default"
-          " dpi %d.", GetDisplayName(is_primary), kDefaultDpi);
-      params.dpi.x = kDefaultDpi;
-      params.dpi.y = kDefaultDpi;
-    }
-  } else {
-    ALOGE("HardwareComposer: Failed to get current %s display config: %d."
-        " Using default display values.",
-        GetDisplayName(is_primary), error.value);
-    params.width = kDefaultDisplayWidth;
-    params.height = kDefaultDisplayHeight;
-    params.dpi.x = kDefaultDpi;
-    params.dpi.y = kDefaultDpi;
-    params.vsync_period_ns = kDefaultVsyncPeriodNs;
-  }
-
-  ALOGI(
-      "HardwareComposer: %s display attributes: width=%d height=%d "
-      "vsync_period_ns=%d DPI=%dx%d",
-      GetDisplayName(is_primary),
-      params.width,
-      params.height,
-      params.vsync_period_ns,
-      params.dpi.x,
-      params.dpi.y);
-
-  return params;
-}
-
-std::string HardwareComposer::Dump() {
-  std::unique_lock<std::mutex> lock(post_thread_mutex_);
-  std::ostringstream stream;
-
-  auto print_display_metrics = [&](const DisplayParams& params) {
-    stream << GetDisplayName(params.is_primary)
-           << " display metrics:     " << params.width << "x"
-           << params.height << " " << (params.dpi.x / 1000.0)
-           << "x" << (params.dpi.y / 1000.0) << " dpi @ "
-           << (1000000000.0 / params.vsync_period_ns) << " Hz"
-           << std::endl;
-  };
-
-  print_display_metrics(primary_display_);
-  if (external_display_)
-    print_display_metrics(*external_display_);
-
-  stream << "Post thread resumed: " << post_thread_resumed_ << std::endl;
-  stream << "Active layers:       " << layers_.size() << std::endl;
-  stream << std::endl;
-
-  for (size_t i = 0; i < layers_.size(); i++) {
-    stream << "Layer " << i << ":";
-    stream << " type=" << layers_[i].GetCompositionType().to_string();
-    stream << " surface_id=" << layers_[i].GetSurfaceId();
-    stream << " buffer_id=" << layers_[i].GetBufferId();
-    stream << std::endl;
-  }
-  stream << std::endl;
-
-  if (post_thread_resumed_) {
-    stream << "Hardware Composer Debug Info:" << std::endl;
-    stream << composer_->dumpDebugInfo();
-  }
-
-  return stream.str();
-}
-
-void HardwareComposer::PostLayers(hwc2_display_t display) {
-  ATRACE_NAME("HardwareComposer::PostLayers");
-
-  // Setup the hardware composer layers with current buffers.
-  for (auto& layer : layers_) {
-    layer.Prepare();
-  }
-
-  // Now that we have taken in a frame from the application, we have a chance
-  // to drop the frame before passing the frame along to HWC.
-  // If the display driver has become backed up, we detect it here and then
-  // react by skipping this frame to catch up latency.
-  while (!retire_fence_fds_.empty() &&
-         (!retire_fence_fds_.front() ||
-          sync_wait(retire_fence_fds_.front().Get(), 0) == 0)) {
-    // There are only 2 fences in here, no performance problem to shift the
-    // array of ints.
-    retire_fence_fds_.erase(retire_fence_fds_.begin());
-  }
-
-  const bool is_fence_pending = static_cast<int32_t>(retire_fence_fds_.size()) >
-                                post_thread_config_.allowed_pending_fence_count;
-
-  if (is_fence_pending) {
-    ATRACE_INT("frame_skip_count", ++frame_skip_count_);
-
-    ALOGW_IF(is_fence_pending,
-             "Warning: dropping a frame to catch up with HWC (pending = %zd)",
-             retire_fence_fds_.size());
-
-    for (auto& layer : layers_) {
-      layer.Drop();
-    }
-    return;
-  } else {
-    // Make the transition more obvious in systrace when the frame skip happens
-    // above.
-    ATRACE_INT("frame_skip_count", 0);
-  }
-
-#if TRACE > 1
-  for (size_t i = 0; i < layers_.size(); i++) {
-    ALOGI("HardwareComposer::PostLayers: layer=%zu buffer_id=%d composition=%s",
-          i, layers_[i].GetBufferId(),
-          layers_[i].GetCompositionType().to_string().c_str());
-  }
-#endif
-
-  HWC::Error error = Validate(display);
-  if (error != HWC::Error::None) {
-    ALOGE("HardwareComposer::PostLayers: Validate failed: %s display=%" PRIu64,
-          error.to_string().c_str(), display);
-    return;
-  }
-
-  error = Present(display);
-  if (error != HWC::Error::None) {
-    ALOGE("HardwareComposer::PostLayers: Present failed: %s",
-          error.to_string().c_str());
-    return;
-  }
-
-  std::vector<Hwc2::Layer> out_layers;
-  std::vector<int> out_fences;
-  error = composer_->getReleaseFences(display,
-                                      &out_layers, &out_fences);
-  ALOGE_IF(error != HWC::Error::None,
-           "HardwareComposer::PostLayers: Failed to get release fences: %s",
-           error.to_string().c_str());
-
-  // Perform post-frame bookkeeping.
-  uint32_t num_elements = out_layers.size();
-  for (size_t i = 0; i < num_elements; ++i) {
-    for (auto& layer : layers_) {
-      if (layer.GetLayerHandle() == out_layers[i]) {
-        layer.Finish(out_fences[i]);
-      }
-    }
-  }
-}
-
-void HardwareComposer::SetDisplaySurfaces(
-    std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces) {
-  ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd",
-        surfaces.size());
-  const bool display_idle = surfaces.size() == 0;
-  {
-    std::unique_lock<std::mutex> lock(post_thread_mutex_);
-    surfaces_ = std::move(surfaces);
-    surfaces_changed_ = true;
-  }
-
-  if (request_display_callback_)
-    request_display_callback_(!display_idle);
-
-  // Set idle state based on whether there are any surfaces to handle.
-  UpdatePostThreadState(PostThreadState::Idle, display_idle);
-}
-
-int HardwareComposer::OnNewGlobalBuffer(DvrGlobalBufferKey key,
-                                        IonBuffer& ion_buffer) {
-  if (key == DvrGlobalBuffers::kVsyncBuffer) {
-    vsync_ring_ = std::make_unique<CPUMappedBroadcastRing<DvrVsyncRing>>(
-        &ion_buffer, CPUUsageMode::WRITE_OFTEN);
-
-    if (vsync_ring_->IsMapped() == false) {
-      return -EPERM;
-    }
-  }
-
-  if (key == DvrGlobalBuffers::kVrFlingerConfigBufferKey) {
-    return MapConfigBuffer(ion_buffer);
-  }
-
-  return 0;
-}
-
-void HardwareComposer::OnDeletedGlobalBuffer(DvrGlobalBufferKey key) {
-  if (key == DvrGlobalBuffers::kVrFlingerConfigBufferKey) {
-    ConfigBufferDeleted();
-  }
-}
-
-int HardwareComposer::MapConfigBuffer(IonBuffer& ion_buffer) {
-  std::lock_guard<std::mutex> lock(shared_config_mutex_);
-  shared_config_ring_ = DvrConfigRing();
-
-  if (ion_buffer.width() < DvrConfigRing::MemorySize()) {
-    ALOGE("HardwareComposer::MapConfigBuffer: invalid buffer size.");
-    return -EINVAL;
-  }
-
-  void* buffer_base = 0;
-  int result = ion_buffer.Lock(ion_buffer.usage(), 0, 0, ion_buffer.width(),
-                               ion_buffer.height(), &buffer_base);
-  if (result != 0) {
-    ALOGE(
-        "HardwareComposer::MapConfigBuffer: Failed to map vrflinger config "
-        "buffer.");
-    return -EPERM;
-  }
-
-  shared_config_ring_ = DvrConfigRing::Create(buffer_base, ion_buffer.width());
-  ion_buffer.Unlock();
-
-  return 0;
-}
-
-void HardwareComposer::ConfigBufferDeleted() {
-  std::lock_guard<std::mutex> lock(shared_config_mutex_);
-  shared_config_ring_ = DvrConfigRing();
-}
-
-void HardwareComposer::UpdateConfigBuffer() {
-  std::lock_guard<std::mutex> lock(shared_config_mutex_);
-  if (!shared_config_ring_.is_valid())
-    return;
-  // Copy from latest record in shared_config_ring_ to local copy.
-  DvrConfig record;
-  if (shared_config_ring_.GetNewest(&shared_config_ring_sequence_, &record)) {
-    ALOGI("DvrConfig updated: sequence %u, post offset %d",
-          shared_config_ring_sequence_, record.frame_post_offset_ns);
-    ++shared_config_ring_sequence_;
-    post_thread_config_ = record;
-  }
-}
-
-int HardwareComposer::PostThreadPollInterruptible(
-    const pdx::LocalHandle& event_fd, int requested_events, int timeout_ms) {
-  pollfd pfd[2] = {
-      {
-          .fd = event_fd.Get(),
-          .events = static_cast<short>(requested_events),
-          .revents = 0,
-      },
-      {
-          .fd = post_thread_event_fd_.Get(),
-          .events = POLLPRI | POLLIN,
-          .revents = 0,
-      },
-  };
-  int ret, error;
-  do {
-    ret = poll(pfd, 2, timeout_ms);
-    error = errno;
-    ALOGW_IF(ret < 0,
-             "HardwareComposer::PostThreadPollInterruptible: Error during "
-             "poll(): %s (%d)",
-             strerror(error), error);
-  } while (ret < 0 && error == EINTR);
-
-  if (ret < 0) {
-    return -error;
-  } else if (ret == 0) {
-    return -ETIMEDOUT;
-  } else if (pfd[0].revents != 0) {
-    return 0;
-  } else if (pfd[1].revents != 0) {
-    ALOGI("VrHwcPost thread interrupted: revents=%x", pfd[1].revents);
-    return kPostThreadInterrupted;
-  } else {
-    return 0;
-  }
-}
-
-// Sleep until the next predicted vsync, returning the predicted vsync
-// timestamp.
-Status<int64_t> HardwareComposer::WaitForPredictedVSync() {
-  const int64_t predicted_vsync_time = last_vsync_timestamp_ +
-      (target_display_->vsync_period_ns * vsync_prediction_interval_);
-  const int error = SleepUntil(predicted_vsync_time);
-  if (error < 0) {
-    ALOGE("HardwareComposer::WaifForVSync:: Failed to sleep: %s",
-          strerror(-error));
-    return error;
-  }
-  return {predicted_vsync_time};
-}
-
-int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
-  const int timer_fd = vsync_sleep_timer_fd_.Get();
-  const itimerspec wakeup_itimerspec = {
-      .it_interval = {.tv_sec = 0, .tv_nsec = 0},
-      .it_value = NsToTimespec(wakeup_timestamp),
-  };
-  int ret =
-      timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &wakeup_itimerspec, nullptr);
-  int error = errno;
-  if (ret < 0) {
-    ALOGE("HardwareComposer::SleepUntil: Failed to set timerfd: %s",
-          strerror(error));
-    return -error;
-  }
-
-  return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN,
-                                     /*timeout_ms*/ -1);
-}
-
-void HardwareComposer::PostThread() {
-  // NOLINTNEXTLINE(runtime/int)
-  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrHwcPost"), 0, 0, 0);
-
-  // Set the scheduler to SCHED_FIFO with high priority. If this fails here
-  // there may have been a startup timing issue between this thread and
-  // performanced. Try again later when this thread becomes active.
-  bool thread_policy_setup =
-      SetThreadPolicy("graphics:high", "/system/performance");
-
-  // Create a timerfd based on CLOCK_MONOTINIC.
-  vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
-  LOG_ALWAYS_FATAL_IF(
-      !vsync_sleep_timer_fd_,
-      "HardwareComposer: Failed to create vsync sleep timerfd: %s",
-      strerror(errno));
-
-  struct VsyncEyeOffsets { int64_t left_ns, right_ns; };
-  bool was_running = false;
-
-  auto get_vsync_eye_offsets = [this]() -> VsyncEyeOffsets {
-    VsyncEyeOffsets offsets;
-    offsets.left_ns =
-        GetPosePredictionTimeOffset(target_display_->vsync_period_ns);
-
-    // TODO(jbates) Query vblank time from device, when such an API is
-    // available. This value (6.3%) was measured on A00 in low persistence mode.
-    int64_t vblank_ns = target_display_->vsync_period_ns * 63 / 1000;
-    offsets.right_ns = (target_display_->vsync_period_ns - vblank_ns) / 2;
-
-    // Check property for overriding right eye offset value.
-    offsets.right_ns =
-        property_get_int64(kRightEyeOffsetProperty, offsets.right_ns);
-
-    return offsets;
-  };
-
-  VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets();
-
-  while (1) {
-    ATRACE_NAME("HardwareComposer::PostThread");
-
-    // Check for updated config once per vsync.
-    UpdateConfigBuffer();
-
-    while (post_thread_quiescent_) {
-      std::unique_lock<std::mutex> lock(post_thread_mutex_);
-      ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
-
-      if (was_running) {
-        vsync_trace_parity_ = false;
-        ATRACE_INT(kVsyncTraceEventName, 0);
-      }
-
-      // Tear down resources.
-      OnPostThreadPaused();
-      was_running = false;
-      post_thread_resumed_ = false;
-      post_thread_ready_.notify_all();
-
-      if (PostThreadCondWait(lock, -1,
-                             [this] { return !post_thread_quiescent_; })) {
-        // A true return value means we've been asked to quit.
-        return;
-      }
-
-      post_thread_resumed_ = true;
-      post_thread_ready_.notify_all();
-
-      ALOGI("HardwareComposer::PostThread: Exiting quiescent state.");
-    }
-
-    if (!composer_)
-      CreateComposer();
-
-    bool target_display_changed = UpdateTargetDisplay();
-    bool just_resumed_running = !was_running;
-    was_running = true;
-
-    if (target_display_changed)
-      vsync_eye_offsets = get_vsync_eye_offsets();
-
-    if (just_resumed_running) {
-      OnPostThreadResumed();
-
-      // Try to setup the scheduler policy if it failed during startup. Only
-      // attempt to do this on transitions from inactive to active to avoid
-      // spamming the system with RPCs and log messages.
-      if (!thread_policy_setup) {
-        thread_policy_setup =
-            SetThreadPolicy("graphics:high", "/system/performance");
-      }
-    }
-
-    if (target_display_changed || just_resumed_running) {
-      // Initialize the last vsync timestamp with the current time. The
-      // predictor below uses this time + the vsync interval in absolute time
-      // units for the initial delay. Once the driver starts reporting vsync the
-      // predictor will sync up with the real vsync.
-      last_vsync_timestamp_ = GetSystemClockNs();
-      vsync_prediction_interval_ = 1;
-      retire_fence_fds_.clear();
-    }
-
-    int64_t vsync_timestamp = 0;
-    {
-      TRACE_FORMAT("wait_vsync|vsync=%u;last_timestamp=%" PRId64
-                   ";prediction_interval=%d|",
-                   vsync_count_ + 1, last_vsync_timestamp_,
-                   vsync_prediction_interval_);
-
-      auto status = WaitForPredictedVSync();
-      ALOGE_IF(
-          !status,
-          "HardwareComposer::PostThread: Failed to wait for vsync event: %s",
-          status.GetErrorMessage().c_str());
-
-      // If there was an error either sleeping was interrupted due to pausing or
-      // there was an error getting the latest timestamp.
-      if (!status)
-        continue;
-
-      // Predicted vsync timestamp for this interval. This is stable because we
-      // use absolute time for the wakeup timer.
-      vsync_timestamp = status.get();
-    }
-
-    vsync_trace_parity_ = !vsync_trace_parity_;
-    ATRACE_INT(kVsyncTraceEventName, vsync_trace_parity_ ? 1 : 0);
-
-    // Advance the vsync counter only if the system is keeping up with hardware
-    // vsync to give clients an indication of the delays.
-    if (vsync_prediction_interval_ == 1)
-      ++vsync_count_;
-
-    UpdateLayerConfig();
-
-    // Publish the vsync event.
-    if (vsync_ring_) {
-      DvrVsync vsync;
-      vsync.vsync_count = vsync_count_;
-      vsync.vsync_timestamp_ns = vsync_timestamp;
-      vsync.vsync_left_eye_offset_ns = vsync_eye_offsets.left_ns;
-      vsync.vsync_right_eye_offset_ns = vsync_eye_offsets.right_ns;
-      vsync.vsync_period_ns = target_display_->vsync_period_ns;
-
-      vsync_ring_->Publish(vsync);
-    }
-
-    {
-      // Sleep until shortly before vsync.
-      ATRACE_NAME("sleep");
-
-      const int64_t display_time_est_ns =
-          vsync_timestamp + target_display_->vsync_period_ns;
-      const int64_t now_ns = GetSystemClockNs();
-      const int64_t sleep_time_ns = display_time_est_ns - now_ns -
-                                    post_thread_config_.frame_post_offset_ns;
-      const int64_t wakeup_time_ns =
-          display_time_est_ns - post_thread_config_.frame_post_offset_ns;
-
-      ATRACE_INT64("sleep_time_ns", sleep_time_ns);
-      if (sleep_time_ns > 0) {
-        int error = SleepUntil(wakeup_time_ns);
-        ALOGE_IF(error < 0 && error != kPostThreadInterrupted,
-                 "HardwareComposer::PostThread: Failed to sleep: %s",
-                 strerror(-error));
-        // If the sleep was interrupted (error == kPostThreadInterrupted),
-        // we still go through and present this frame because we may have set
-        // layers earlier and we want to flush the Composer's internal command
-        // buffer by continuing through to validate and present.
-      }
-    }
-
-    {
-      auto status = composer_callback_->GetVsyncTime(target_display_->id);
-
-      // If we failed to read vsync there might be a problem with the driver.
-      // Since there's nothing we can do just behave as though we didn't get an
-      // updated vsync time and let the prediction continue.
-      const int64_t current_vsync_timestamp =
-          status ? status.get() : last_vsync_timestamp_;
-
-      const bool vsync_delayed =
-          last_vsync_timestamp_ == current_vsync_timestamp;
-      ATRACE_INT("vsync_delayed", vsync_delayed);
-
-      // If vsync was delayed advance the prediction interval and allow the
-      // fence logic in PostLayers() to skip the frame.
-      if (vsync_delayed) {
-        ALOGW(
-            "HardwareComposer::PostThread: VSYNC timestamp did not advance "
-            "since last frame: timestamp=%" PRId64 " prediction_interval=%d",
-            current_vsync_timestamp, vsync_prediction_interval_);
-        vsync_prediction_interval_++;
-      } else {
-        // We have an updated vsync timestamp, reset the prediction interval.
-        last_vsync_timestamp_ = current_vsync_timestamp;
-        vsync_prediction_interval_ = 1;
-      }
-    }
-
-    PostLayers(target_display_->id);
-  }
-}
-
-bool HardwareComposer::UpdateTargetDisplay() {
-  bool target_display_changed = false;
-  auto displays = composer_callback_->GetDisplays();
-  if (displays.external_display_was_hotplugged) {
-    bool was_using_external_display = !target_display_->is_primary;
-    if (was_using_external_display) {
-      // The external display was hotplugged, so make sure to ignore any bad
-      // display errors as we destroy the layers.
-      for (auto& layer: layers_)
-        layer.IgnoreBadDisplayErrorsOnDestroy(true);
-    }
-
-    if (displays.external_display) {
-      // External display was connected
-      external_display_ = GetDisplayParams(composer_.get(),
-          *displays.external_display, /*is_primary*/ false);
-
-      ALOGI("External display connected. Switching to external display.");
-      target_display_ = &(*external_display_);
-      target_display_changed = true;
-    } else {
-      // External display was disconnected
-      external_display_ = std::nullopt;
-      if (was_using_external_display) {
-        ALOGI("External display disconnected. Switching to primary display.");
-        target_display_ = &primary_display_;
-        target_display_changed = true;
-      }
-    }
-  }
-
-  if (target_display_changed) {
-    // If we're switching to the external display, turn the primary display off.
-    if (!target_display_->is_primary) {
-      EnableDisplay(primary_display_, false);
-    }
-    // If we're switching to the primary display, and the external display is
-    // still connected, turn the external display off.
-    else if (target_display_->is_primary && external_display_) {
-      EnableDisplay(*external_display_, false);
-    }
-
-    // Update the cached edid data for the current display.
-    UpdateEdidData(composer_.get(), target_display_->id);
-
-    // Turn the new target display on.
-    EnableDisplay(*target_display_, true);
-
-    // When we switch displays we need to recreate all the layers, so clear the
-    // current list, which will trigger layer recreation.
-    layers_.clear();
-  }
-
-  return target_display_changed;
-}
-
-// Checks for changes in the surface stack and updates the layer config to
-// accomodate the new stack.
-void HardwareComposer::UpdateLayerConfig() {
-  std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces;
-  {
-    std::unique_lock<std::mutex> lock(post_thread_mutex_);
-
-    if (!surfaces_changed_ && (!layers_.empty() || surfaces_.empty()))
-      return;
-
-    surfaces = surfaces_;
-    surfaces_changed_ = false;
-  }
-
-  ATRACE_NAME("UpdateLayerConfig_HwLayers");
-
-  // Sort the new direct surface list by z-order to determine the relative order
-  // of the surfaces. This relative order is used for the HWC z-order value to
-  // insulate VrFlinger and HWC z-order semantics from each other.
-  std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
-    return a->z_order() < b->z_order();
-  });
-
-  // Prepare a new layer stack, pulling in layers from the previous
-  // layer stack that are still active and updating their attributes.
-  std::vector<Layer> layers;
-  size_t layer_index = 0;
-  for (const auto& surface : surfaces) {
-    // The bottom layer is opaque, other layers blend.
-    HWC::BlendMode blending =
-        layer_index == 0 ? HWC::BlendMode::None : HWC::BlendMode::Coverage;
-
-    // Try to find a layer for this surface in the set of active layers.
-    auto search =
-        std::lower_bound(layers_.begin(), layers_.end(), surface->surface_id());
-    const bool found = search != layers_.end() &&
-                       search->GetSurfaceId() == surface->surface_id();
-    if (found) {
-      // Update the attributes of the layer that may have changed.
-      search->SetBlending(blending);
-      search->SetZOrder(layer_index);  // Relative z-order.
-
-      // Move the existing layer to the new layer set and remove the empty layer
-      // object from the current set.
-      layers.push_back(std::move(*search));
-      layers_.erase(search);
-    } else {
-      // Insert a layer for the new surface.
-      layers.emplace_back(composer_.get(), *target_display_, surface, blending,
-          HWC::Composition::Device, layer_index);
-    }
-
-    ALOGI_IF(
-        TRACE,
-        "HardwareComposer::UpdateLayerConfig: layer_index=%zu surface_id=%d",
-        layer_index, layers[layer_index].GetSurfaceId());
-
-    layer_index++;
-  }
-
-  // Sort the new layer stack by ascending surface id.
-  std::sort(layers.begin(), layers.end());
-
-  // Replace the previous layer set with the new layer set. The destructor of
-  // the previous set will clean up the remaining Layers that are not moved to
-  // the new layer set.
-  layers_ = std::move(layers);
-
-  ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
-           layers_.size());
-}
-
-std::vector<sp<IVsyncCallback>>::const_iterator
-HardwareComposer::VsyncService::FindCallback(
-    const sp<IVsyncCallback>& callback) const {
-  sp<IBinder> binder = IInterface::asBinder(callback);
-  return std::find_if(callbacks_.cbegin(), callbacks_.cend(),
-                      [&](const sp<IVsyncCallback>& callback) {
-                        return IInterface::asBinder(callback) == binder;
-                      });
-}
-
-status_t HardwareComposer::VsyncService::registerCallback(
-    const sp<IVsyncCallback> callback) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  if (FindCallback(callback) == callbacks_.cend()) {
-    callbacks_.push_back(callback);
-  }
-  return OK;
-}
-
-status_t HardwareComposer::VsyncService::unregisterCallback(
-    const sp<IVsyncCallback> callback) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  auto iter = FindCallback(callback);
-  if (iter != callbacks_.cend()) {
-    callbacks_.erase(iter);
-  }
-  return OK;
-}
-
-void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
-  ATRACE_NAME("VsyncService::OnVsync");
-  std::lock_guard<std::mutex> autolock(mutex_);
-  for (auto iter = callbacks_.begin(); iter != callbacks_.end();) {
-    if ((*iter)->onVsync(vsync_timestamp) == android::DEAD_OBJECT) {
-      iter = callbacks_.erase(iter);
-    } else {
-      ++iter;
-    }
-  }
-}
-
-Return<void> HardwareComposer::ComposerCallback::onHotplug(
-    Hwc2::Display display, IComposerCallback::Connection conn) {
-  std::lock_guard<std::mutex> lock(mutex_);
-  ALOGI("onHotplug display=%" PRIu64 " conn=%d", display, conn);
-
-  bool is_primary = !got_first_hotplug_ || display == primary_display_.id;
-
-  // Our first onHotplug callback is always for the primary display.
-  if (!got_first_hotplug_) {
-    LOG_ALWAYS_FATAL_IF(conn != IComposerCallback::Connection::CONNECTED,
-        "Initial onHotplug callback should be primary display connected");
-    got_first_hotplug_ = true;
-  } else if (is_primary) {
-    ALOGE("Ignoring unexpected onHotplug() call for primary display");
-    return Void();
-  }
-
-  if (conn == IComposerCallback::Connection::CONNECTED) {
-    if (!is_primary)
-      external_display_ = DisplayInfo();
-    DisplayInfo& display_info = is_primary ?
-        primary_display_ : *external_display_;
-    display_info.id = display;
-
-    std::array<char, 1024> buffer;
-    snprintf(buffer.data(), buffer.size(),
-             "/sys/class/graphics/fb%" PRIu64 "/vsync_event", display);
-    if (LocalHandle handle{buffer.data(), O_RDONLY}) {
-      ALOGI(
-          "HardwareComposer::ComposerCallback::onHotplug: Driver supports "
-          "vsync_event node for display %" PRIu64,
-          display);
-      display_info.driver_vsync_event_fd = std::move(handle);
-    } else {
-      ALOGI(
-          "HardwareComposer::ComposerCallback::onHotplug: Driver does not "
-          "support vsync_event node for display %" PRIu64,
-          display);
-    }
-  } else if (conn == IComposerCallback::Connection::DISCONNECTED) {
-    external_display_ = std::nullopt;
-  }
-
-  if (!is_primary)
-    external_display_was_hotplugged_ = true;
-
-  return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onRefresh(
-    Hwc2::Display /*display*/) {
-  return hardware::Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
-                                                         int64_t timestamp) {
-  TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
-               display, timestamp);
-  std::lock_guard<std::mutex> lock(mutex_);
-  DisplayInfo* display_info = GetDisplayInfo(display);
-  if (display_info) {
-    display_info->callback_vsync_timestamp = timestamp;
-  }
-  if (primary_display_.id == display && vsync_service_ != nullptr) {
-    vsync_service_->OnVsync(timestamp);
-  }
-
-  return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsync_2_4(
-    Hwc2::Display /*display*/, int64_t /*timestamp*/,
-    Hwc2::VsyncPeriodNanos /*vsyncPeriodNanos*/) {
-  LOG_ALWAYS_FATAL("Unexpected onVsync_2_4 callback");
-  return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsyncPeriodTimingChanged(
-    Hwc2::Display /*display*/,
-    const Hwc2::VsyncPeriodChangeTimeline& /*updatedTimeline*/) {
-  LOG_ALWAYS_FATAL("Unexpected onVsyncPeriodTimingChanged callback");
-  return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onSeamlessPossible(
-    Hwc2::Display /*display*/) {
-  LOG_ALWAYS_FATAL("Unexpected onSeamlessPossible callback");
-  return Void();
-}
-
-void HardwareComposer::ComposerCallback::SetVsyncService(
-    const sp<VsyncService>& vsync_service) {
-  std::lock_guard<std::mutex> lock(mutex_);
-  vsync_service_ = vsync_service;
-}
-
-HardwareComposer::ComposerCallback::Displays
-HardwareComposer::ComposerCallback::GetDisplays() {
-  std::lock_guard<std::mutex> lock(mutex_);
-  Displays displays;
-  displays.primary_display = primary_display_.id;
-  if (external_display_)
-    displays.external_display = external_display_->id;
-  if (external_display_was_hotplugged_) {
-    external_display_was_hotplugged_ = false;
-    displays.external_display_was_hotplugged = true;
-  }
-  return displays;
-}
-
-Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
-    hwc2_display_t display) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  DisplayInfo* display_info = GetDisplayInfo(display);
-  if (!display_info) {
-    ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display);
-    return ErrorStatus(EINVAL);
-  }
-
-  // See if the driver supports direct vsync events.
-  LocalHandle& event_fd = display_info->driver_vsync_event_fd;
-  if (!event_fd) {
-    // Fall back to returning the last timestamp returned by the vsync
-    // callback.
-    return display_info->callback_vsync_timestamp;
-  }
-
-  // When the driver supports the vsync_event sysfs node we can use it to
-  // determine the latest vsync timestamp, even if the HWC callback has been
-  // delayed.
-
-  // The driver returns data in the form "VSYNC=<timestamp ns>".
-  std::array<char, 32> data;
-  data.fill('\0');
-
-  // Seek back to the beginning of the event file.
-  int ret = lseek(event_fd.Get(), 0, SEEK_SET);
-  if (ret < 0) {
-    const int error = errno;
-    ALOGE(
-        "HardwareComposer::ComposerCallback::GetVsyncTime: Failed to seek "
-        "vsync event fd: %s",
-        strerror(error));
-    return ErrorStatus(error);
-  }
-
-  // Read the vsync event timestamp.
-  ret = read(event_fd.Get(), data.data(), data.size());
-  if (ret < 0) {
-    const int error = errno;
-    ALOGE_IF(error != EAGAIN,
-             "HardwareComposer::ComposerCallback::GetVsyncTime: Error "
-             "while reading timestamp: %s",
-             strerror(error));
-    return ErrorStatus(error);
-  }
-
-  int64_t timestamp;
-  ret = sscanf(data.data(), "VSYNC=%" PRIu64,
-               reinterpret_cast<uint64_t*>(&timestamp));
-  if (ret < 0) {
-    const int error = errno;
-    ALOGE(
-        "HardwareComposer::ComposerCallback::GetVsyncTime: Error while "
-        "parsing timestamp: %s",
-        strerror(error));
-    return ErrorStatus(error);
-  }
-
-  return {timestamp};
-}
-
-HardwareComposer::ComposerCallback::DisplayInfo*
-HardwareComposer::ComposerCallback::GetDisplayInfo(hwc2_display_t display) {
-  if (display == primary_display_.id) {
-    return &primary_display_;
-  } else if (external_display_ && display == external_display_->id) {
-    return &(*external_display_);
-  }
-  return nullptr;
-}
-
-void Layer::Reset() {
-  if (hardware_composer_layer_) {
-    HWC::Error error =
-        composer_->destroyLayer(display_params_.id, hardware_composer_layer_);
-    if (error != HWC::Error::None &&
-        (!ignore_bad_display_errors_on_destroy_ ||
-         error != HWC::Error::BadDisplay)) {
-      ALOGE("destroyLayer() failed for display %" PRIu64 ", layer %" PRIu64
-          ". error: %s", display_params_.id, hardware_composer_layer_,
-          error.to_string().c_str());
-    }
-    hardware_composer_layer_ = 0;
-  }
-
-  z_order_ = 0;
-  blending_ = HWC::BlendMode::None;
-  composition_type_ = HWC::Composition::Invalid;
-  target_composition_type_ = composition_type_;
-  source_ = EmptyVariant{};
-  acquire_fence_.Close();
-  surface_rect_functions_applied_ = false;
-  pending_visibility_settings_ = true;
-  cached_buffer_map_.clear();
-  ignore_bad_display_errors_on_destroy_ = false;
-}
-
-Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
-             const std::shared_ptr<DirectDisplaySurface>& surface,
-             HWC::BlendMode blending, HWC::Composition composition_type,
-             size_t z_order)
-    : composer_(composer),
-      display_params_(display_params),
-      z_order_{z_order},
-      blending_{blending},
-      target_composition_type_{composition_type},
-      source_{SourceSurface{surface}} {
-  CommonLayerSetup();
-}
-
-Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
-             const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
-             HWC::Composition composition_type, size_t z_order)
-    : composer_(composer),
-      display_params_(display_params),
-      z_order_{z_order},
-      blending_{blending},
-      target_composition_type_{composition_type},
-      source_{SourceBuffer{buffer}} {
-  CommonLayerSetup();
-}
-
-Layer::~Layer() { Reset(); }
-
-Layer::Layer(Layer&& other) noexcept { *this = std::move(other); }
-
-Layer& Layer::operator=(Layer&& other) noexcept {
-  if (this != &other) {
-    Reset();
-    using std::swap;
-    swap(composer_, other.composer_);
-    swap(display_params_, other.display_params_);
-    swap(hardware_composer_layer_, other.hardware_composer_layer_);
-    swap(z_order_, other.z_order_);
-    swap(blending_, other.blending_);
-    swap(composition_type_, other.composition_type_);
-    swap(target_composition_type_, other.target_composition_type_);
-    swap(source_, other.source_);
-    swap(acquire_fence_, other.acquire_fence_);
-    swap(surface_rect_functions_applied_,
-         other.surface_rect_functions_applied_);
-    swap(pending_visibility_settings_, other.pending_visibility_settings_);
-    swap(cached_buffer_map_, other.cached_buffer_map_);
-    swap(ignore_bad_display_errors_on_destroy_,
-         other.ignore_bad_display_errors_on_destroy_);
-  }
-  return *this;
-}
-
-void Layer::UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer) {
-  if (source_.is<SourceBuffer>())
-    std::get<SourceBuffer>(source_) = {buffer};
-}
-
-void Layer::SetBlending(HWC::BlendMode blending) {
-  if (blending_ != blending) {
-    blending_ = blending;
-    pending_visibility_settings_ = true;
-  }
-}
-
-void Layer::SetZOrder(size_t z_order) {
-  if (z_order_ != z_order) {
-    z_order_ = z_order;
-    pending_visibility_settings_ = true;
-  }
-}
-
-IonBuffer* Layer::GetBuffer() {
-  struct Visitor {
-    IonBuffer* operator()(SourceSurface& source) { return source.GetBuffer(); }
-    IonBuffer* operator()(SourceBuffer& source) { return source.GetBuffer(); }
-    IonBuffer* operator()(EmptyVariant) { return nullptr; }
-  };
-  return source_.Visit(Visitor{});
-}
-
-void Layer::UpdateVisibilitySettings() {
-  if (pending_visibility_settings_) {
-    pending_visibility_settings_ = false;
-
-    HWC::Error error;
-
-    error = composer_->setLayerBlendMode(
-        display_params_.id, hardware_composer_layer_,
-        blending_.cast<Hwc2::IComposerClient::BlendMode>());
-    ALOGE_IF(error != HWC::Error::None,
-             "Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
-             error.to_string().c_str());
-
-    error = composer_->setLayerZOrder(display_params_.id,
-        hardware_composer_layer_, z_order_);
-    ALOGE_IF(error != HWC::Error::None,
-             "Layer::UpdateLayerSettings: Error setting z_ order: %s",
-             error.to_string().c_str());
-  }
-}
-
-void Layer::UpdateLayerSettings() {
-  HWC::Error error;
-
-  UpdateVisibilitySettings();
-
-  // TODO(eieio): Use surface attributes or some other mechanism to control
-  // the layer display frame.
-  error = composer_->setLayerDisplayFrame(
-      display_params_.id, hardware_composer_layer_,
-      {0, 0, display_params_.width, display_params_.height});
-  ALOGE_IF(error != HWC::Error::None,
-           "Layer::UpdateLayerSettings: Error setting layer display frame: %s",
-           error.to_string().c_str());
-
-  error = composer_->setLayerVisibleRegion(
-      display_params_.id, hardware_composer_layer_,
-      {{0, 0, display_params_.width, display_params_.height}});
-  ALOGE_IF(error != HWC::Error::None,
-           "Layer::UpdateLayerSettings: Error setting layer visible region: %s",
-           error.to_string().c_str());
-
-  error = composer_->setLayerPlaneAlpha(display_params_.id,
-      hardware_composer_layer_, 1.0f);
-  ALOGE_IF(error != HWC::Error::None,
-           "Layer::UpdateLayerSettings: Error setting layer plane alpha: %s",
-           error.to_string().c_str());
-}
-
-void Layer::CommonLayerSetup() {
-  HWC::Error error = composer_->createLayer(display_params_.id,
-                                            &hardware_composer_layer_);
-  ALOGE_IF(error != HWC::Error::None,
-           "Layer::CommonLayerSetup: Failed to create layer on primary "
-           "display: %s",
-           error.to_string().c_str());
-  UpdateLayerSettings();
-}
-
-bool Layer::CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id) {
-  auto search = cached_buffer_map_.find(slot);
-  if (search != cached_buffer_map_.end() && search->second == buffer_id)
-    return true;
-
-  // Assign or update the buffer slot.
-  if (buffer_id >= 0)
-    cached_buffer_map_[slot] = buffer_id;
-  return false;
-}
-
-void Layer::Prepare() {
-  int right, bottom, id;
-  sp<GraphicBuffer> handle;
-  std::size_t slot;
-
-  // Acquire the next buffer according to the type of source.
-  IfAnyOf<SourceSurface, SourceBuffer>::Call(&source_, [&](auto& source) {
-    std::tie(right, bottom, id, handle, acquire_fence_, slot) =
-        source.Acquire();
-  });
-
-  TRACE_FORMAT("Layer::Prepare|buffer_id=%d;slot=%zu|", id, slot);
-
-  // Update any visibility (blending, z-order) changes that occurred since
-  // last prepare.
-  UpdateVisibilitySettings();
-
-  // When a layer is first setup there may be some time before the first
-  // buffer arrives. Setup the HWC layer as a solid color to stall for time
-  // until the first buffer arrives. Once the first buffer arrives there will
-  // always be a buffer for the frame even if it is old.
-  if (!handle.get()) {
-    if (composition_type_ == HWC::Composition::Invalid) {
-      composition_type_ = HWC::Composition::SolidColor;
-      composer_->setLayerCompositionType(
-          display_params_.id, hardware_composer_layer_,
-          composition_type_.cast<Hwc2::IComposerClient::Composition>());
-      Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0};
-      composer_->setLayerColor(display_params_.id, hardware_composer_layer_,
-                               layer_color);
-    } else {
-      // The composition type is already set. Nothing else to do until a
-      // buffer arrives.
-    }
-  } else {
-    if (composition_type_ != target_composition_type_) {
-      composition_type_ = target_composition_type_;
-      composer_->setLayerCompositionType(
-          display_params_.id, hardware_composer_layer_,
-          composition_type_.cast<Hwc2::IComposerClient::Composition>());
-    }
-
-    // See if the HWC cache already has this buffer.
-    const bool cached = CheckAndUpdateCachedBuffer(slot, id);
-    if (cached)
-      handle = nullptr;
-
-    HWC::Error error{HWC::Error::None};
-    error =
-        composer_->setLayerBuffer(display_params_.id, hardware_composer_layer_,
-                                  slot, handle, acquire_fence_.Get());
-
-    ALOGE_IF(error != HWC::Error::None,
-             "Layer::Prepare: Error setting layer buffer: %s",
-             error.to_string().c_str());
-
-    if (!surface_rect_functions_applied_) {
-      const float float_right = right;
-      const float float_bottom = bottom;
-      error = composer_->setLayerSourceCrop(display_params_.id,
-                                            hardware_composer_layer_,
-                                            {0, 0, float_right, float_bottom});
-
-      ALOGE_IF(error != HWC::Error::None,
-               "Layer::Prepare: Error setting layer source crop: %s",
-               error.to_string().c_str());
-
-      surface_rect_functions_applied_ = true;
-    }
-  }
-}
-
-void Layer::Finish(int release_fence_fd) {
-  IfAnyOf<SourceSurface, SourceBuffer>::Call(
-      &source_, [release_fence_fd](auto& source) {
-        source.Finish(LocalHandle(release_fence_fd));
-      });
-}
-
-void Layer::Drop() { acquire_fence_.Close(); }
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
deleted file mode 100644
index bfce10b..0000000
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ /dev/null
@@ -1,577 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
-
-#include <ui/GraphicBuffer.h>
-#include "DisplayHardware/ComposerHal.h"
-#include "hwc_types.h"
-
-#include <dvr/dvr_shared_buffers.h>
-#include <hardware/gralloc.h>
-#include <log/log.h>
-
-#include <array>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <thread>
-#include <tuple>
-#include <vector>
-
-#include <dvr/dvr_config.h>
-#include <dvr/dvr_vsync.h>
-#include <pdx/file_handle.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/shared_buffer_helpers.h>
-#include <private/dvr/vsync_service.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
-#include "acquired_buffer.h"
-#include "display_surface.h"
-
-// Hardware composer HAL doesn't define HWC_TRANSFORM_NONE as of this writing.
-#ifndef HWC_TRANSFORM_NONE
-#define HWC_TRANSFORM_NONE static_cast<hwc_transform_t>(0)
-#endif
-
-namespace android {
-namespace dvr {
-
-// Basic display metrics for physical displays.
-struct DisplayParams {
-  hwc2_display_t id;
-  bool is_primary;
-
-  int width;
-  int height;
-
-  struct {
-    int x;
-    int y;
-  } dpi;
-
-  int vsync_period_ns;
-};
-
-// Layer represents the connection between a hardware composer layer and the
-// source supplying buffers for the layer's contents.
-class Layer {
- public:
-  Layer() = default;
-
-  // Sets up the layer to use a display surface as its content source. The Layer
-  // automatically handles ACQUIRE/RELEASE phases for the surface's buffer train
-  // every frame.
-  //
-  // |composer| The composer instance.
-  // |display_params| Info about the display to use.
-  // |blending| receives HWC_BLENDING_* values.
-  // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
-  // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
-  // |index| is the index of this surface in the DirectDisplaySurface array.
-  Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
-        const std::shared_ptr<DirectDisplaySurface>& surface,
-        HWC::BlendMode blending, HWC::Composition composition_type,
-        size_t z_order);
-
-  // Sets up the layer to use a direct buffer as its content source. No special
-  // handling of the buffer is performed; responsibility for updating or
-  // changing the buffer each frame is on the caller.
-  //
-  // |composer| The composer instance.
-  // |display_params| Info about the display to use.
-  // |blending| receives HWC_BLENDING_* values.
-  // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
-  // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
-  Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
-        const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
-        HWC::Composition composition_type, size_t z_order);
-
-  Layer(Layer&&) noexcept;
-  Layer& operator=(Layer&&) noexcept;
-
-  ~Layer();
-
-  // Releases any shared pointers and fence handles held by this instance.
-  void Reset();
-
-  // Layers that use a direct IonBuffer should call this each frame to update
-  // which buffer will be used for the next PostLayers.
-  void UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer);
-
-  // Sets up the hardware composer layer for the next frame. When the layer is
-  // associated with a display surface, this method automatically ACQUIRES a new
-  // buffer if one is available.
-  void Prepare();
-
-  // After calling prepare, if this frame is to be dropped instead of passing
-  // along to the HWC, call Drop to close the contained fence(s).
-  void Drop();
-
-  // Performs fence bookkeeping after the frame has been posted to hardware
-  // composer.
-  void Finish(int release_fence_fd);
-
-  // Sets the blending for the layer. |blending| receives HWC_BLENDING_* values.
-  void SetBlending(HWC::BlendMode blending);
-
-  // Sets the z-order of this layer
-  void SetZOrder(size_t z_order);
-
-  // Gets the current IonBuffer associated with this layer. Ownership of the
-  // buffer DOES NOT pass to the caller and the pointer is not guaranteed to
-  // remain valid across calls to Layer::Setup(), Layer::Prepare(), or
-  // Layer::Reset(). YOU HAVE BEEN WARNED.
-  IonBuffer* GetBuffer();
-
-  HWC::Composition GetCompositionType() const { return composition_type_; }
-  HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; }
-  bool IsLayerSetup() const { return !source_.empty(); }
-
-  int GetSurfaceId() const {
-    int surface_id = -1;
-    pdx::rpc::IfAnyOf<SourceSurface>::Call(
-        &source_, [&surface_id](const SourceSurface& surface_source) {
-          surface_id = surface_source.GetSurfaceId();
-        });
-    return surface_id;
-  }
-
-  int GetBufferId() const {
-    int buffer_id = -1;
-    pdx::rpc::IfAnyOf<SourceSurface>::Call(
-        &source_, [&buffer_id](const SourceSurface& surface_source) {
-          buffer_id = surface_source.GetBufferId();
-        });
-    return buffer_id;
-  }
-
-  // Compares Layers by surface id.
-  bool operator<(const Layer& other) const {
-    return GetSurfaceId() < other.GetSurfaceId();
-  }
-  bool operator<(int surface_id) const { return GetSurfaceId() < surface_id; }
-
-  void IgnoreBadDisplayErrorsOnDestroy(bool ignore) {
-    ignore_bad_display_errors_on_destroy_ = ignore;
-  }
-
- private:
-  void CommonLayerSetup();
-
-  // Applies all of the settings to this layer using the hwc functions
-  void UpdateLayerSettings();
-
-  // Applies visibility settings that may have changed.
-  void UpdateVisibilitySettings();
-
-  // Checks whether the buffer, given by id, is associated with the given slot
-  // in the HWC buffer cache. If the slot is not associated with the given
-  // buffer the cache is updated to establish the association and the buffer
-  // should be sent to HWC using setLayerBuffer. Returns true if the association
-  // was already established, false if not. A buffer_id of -1 is never
-  // associated and always returns false.
-  bool CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id);
-
-  // Composer instance.
-  Hwc2::Composer* composer_ = nullptr;
-
-  // Parameters of the display to use for this layer.
-  DisplayParams display_params_;
-
-  // The hardware composer layer and metrics to use during the prepare cycle.
-  hwc2_layer_t hardware_composer_layer_ = 0;
-
-  // Layer properties used to setup the hardware composer layer during the
-  // Prepare phase.
-  size_t z_order_ = 0;
-  HWC::BlendMode blending_ = HWC::BlendMode::None;
-  HWC::Composition composition_type_ = HWC::Composition::Invalid;
-  HWC::Composition target_composition_type_ = HWC::Composition::Device;
-
-  // State when the layer is connected to a surface. Provides the same interface
-  // as SourceBuffer to simplify internal use by Layer.
-  struct SourceSurface {
-    std::shared_ptr<DirectDisplaySurface> surface;
-    AcquiredBuffer acquired_buffer;
-    pdx::LocalHandle release_fence;
-
-    explicit SourceSurface(const std::shared_ptr<DirectDisplaySurface>& surface)
-        : surface(surface) {}
-
-    // Attempts to acquire a new buffer from the surface and return a tuple with
-    // width, height, buffer handle, and fence. If a new buffer is not available
-    // the previous buffer is returned or an empty value if no buffer has ever
-    // been posted. When a new buffer is acquired the previous buffer's release
-    // fence is passed out automatically.
-    std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
-    Acquire() {
-      if (surface->IsBufferAvailable()) {
-        acquired_buffer.Release(std::move(release_fence));
-        acquired_buffer = surface->AcquireCurrentBuffer();
-        ATRACE_ASYNC_END("BufferPost", acquired_buffer.buffer()->id());
-      }
-      if (!acquired_buffer.IsEmpty()) {
-        return std::make_tuple(
-            acquired_buffer.buffer()->width(),
-            acquired_buffer.buffer()->height(), acquired_buffer.buffer()->id(),
-            acquired_buffer.buffer()->buffer()->buffer(),
-            acquired_buffer.ClaimAcquireFence(), acquired_buffer.slot());
-      } else {
-        return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
-      }
-    }
-
-    void Finish(pdx::LocalHandle fence) { release_fence = std::move(fence); }
-
-    // Gets a pointer to the current acquired buffer or returns nullptr if there
-    // isn't one.
-    IonBuffer* GetBuffer() {
-      if (acquired_buffer.IsAvailable())
-        return acquired_buffer.buffer()->buffer();
-      else
-        return nullptr;
-    }
-
-    // Returns the surface id of the surface.
-    int GetSurfaceId() const { return surface->surface_id(); }
-
-    // Returns the buffer id for the current buffer.
-    int GetBufferId() const {
-      if (acquired_buffer.IsAvailable())
-        return acquired_buffer.buffer()->id();
-      else
-        return -1;
-    }
-  };
-
-  // State when the layer is connected to a buffer. Provides the same interface
-  // as SourceSurface to simplify internal use by Layer.
-  struct SourceBuffer {
-    std::shared_ptr<IonBuffer> buffer;
-
-    std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
-    Acquire() {
-      if (buffer)
-        return std::make_tuple(buffer->width(), buffer->height(), -1,
-                               buffer->buffer(), pdx::LocalHandle{}, 0);
-      else
-        return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
-    }
-
-    void Finish(pdx::LocalHandle /*fence*/) {}
-
-    IonBuffer* GetBuffer() { return buffer.get(); }
-
-    int GetSurfaceId() const { return -1; }
-    int GetBufferId() const { return -1; }
-  };
-
-  // The underlying hardware composer layer is supplied buffers either from a
-  // surface buffer train or from a buffer directly.
-  pdx::rpc::Variant<SourceSurface, SourceBuffer> source_;
-
-  pdx::LocalHandle acquire_fence_;
-  bool surface_rect_functions_applied_ = false;
-  bool pending_visibility_settings_ = true;
-
-  // Map of buffer slot assignments that have already been established with HWC:
-  // slot -> buffer_id. When this map contains a matching slot and buffer_id the
-  // buffer argument to setLayerBuffer may be nullptr to avoid the cost of
-  // importing a buffer HWC already knows about.
-  std::map<std::size_t, int> cached_buffer_map_;
-
-  // When calling destroyLayer() on an external display that's been removed we
-  // typically get HWC2_ERROR_BAD_DISPLAY errors. If
-  // ignore_bad_display_errors_on_destroy_ is true, don't log the bad display
-  // errors, since they're expected.
-  bool ignore_bad_display_errors_on_destroy_ = false;
-
-  Layer(const Layer&) = delete;
-  void operator=(const Layer&) = delete;
-};
-
-// HardwareComposer encapsulates the hardware composer HAL, exposing a
-// simplified API to post buffers to the display.
-//
-// HardwareComposer is accessed by both the vr flinger dispatcher thread and the
-// surface flinger main thread, in addition to internally running a separate
-// thread for compositing/EDS and posting layers to the HAL. When changing how
-// variables are used or adding new state think carefully about which threads
-// will access the state and whether it needs to be synchronized.
-class HardwareComposer {
- public:
-  using RequestDisplayCallback = std::function<void(bool)>;
-
-  HardwareComposer();
-  ~HardwareComposer();
-
-  bool Initialize(Hwc2::Composer* composer,
-                  hwc2_display_t primary_display_id,
-                  RequestDisplayCallback request_display_callback);
-
-  bool IsInitialized() const { return initialized_; }
-
-  // Start the post thread if there's work to do (i.e. visible layers). This
-  // should only be called from surface flinger's main thread.
-  void Enable();
-  // Pause the post thread, blocking until the post thread has signaled that
-  // it's paused. This should only be called from surface flinger's main thread.
-  void Disable();
-
-  // Called on a binder thread.
-  void OnBootFinished();
-
-  std::string Dump();
-
-  const DisplayParams& GetPrimaryDisplayParams() const {
-    return primary_display_;
-  }
-
-  // Sets the display surfaces to compose the hardware layer stack.
-  void SetDisplaySurfaces(
-      std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces);
-
-  int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
-  void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
-
-  // Gets the edid data for the current active display (internal or external)
-  DisplayIdentificationData GetCurrentDisplayIdentificationData() {
-    return display_identification_data_;
-  }
-
-  // Gets the edid port for the current active display (internal or external)
-  uint8_t GetCurrentDisplayPort() { return display_port_; }
-
- private:
-  DisplayParams GetDisplayParams(Hwc2::Composer* composer,
-      hwc2_display_t display, bool is_primary);
-
-  // Turn display vsync on/off. Returns true on success, false on failure.
-  bool EnableVsync(const DisplayParams& display, bool enabled);
-  // Turn display power on/off. Returns true on success, false on failure.
-  bool SetPowerMode(const DisplayParams& display, bool active);
-  // Convenience function to turn a display on/off. Turns both power and vsync
-  // on/off. Returns true on success, false on failure.
-  bool EnableDisplay(const DisplayParams& display, bool enabled);
-
-  class VsyncService : public BnVsyncService {
-   public:
-    status_t registerCallback(const sp<IVsyncCallback> callback) override;
-    status_t unregisterCallback(const sp<IVsyncCallback> callback) override;
-    void OnVsync(int64_t vsync_timestamp);
-   private:
-    std::vector<sp<IVsyncCallback>>::const_iterator FindCallback(
-        const sp<IVsyncCallback>& callback) const;
-    std::mutex mutex_;
-    std::vector<sp<IVsyncCallback>> callbacks_;
-  };
-
-  class ComposerCallback : public Hwc2::IComposerCallback {
-   public:
-    ComposerCallback() = default;
-    hardware::Return<void> onHotplug(Hwc2::Display display,
-                                     Connection conn) override;
-    hardware::Return<void> onRefresh(Hwc2::Display display) override;
-    hardware::Return<void> onVsync(Hwc2::Display display,
-                                   int64_t timestamp) override;
-    hardware::Return<void> onVsync_2_4(
-        Hwc2::Display display, int64_t timestamp,
-        Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override;
-    hardware::Return<void> onVsyncPeriodTimingChanged(
-        Hwc2::Display display,
-        const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
-    hardware::Return<void> onSeamlessPossible(Hwc2::Display display) override;
-
-    bool GotFirstHotplug() { return got_first_hotplug_; }
-    void SetVsyncService(const sp<VsyncService>& vsync_service);
-
-    struct Displays {
-      hwc2_display_t primary_display = 0;
-      std::optional<hwc2_display_t> external_display;
-      bool external_display_was_hotplugged = false;
-    };
-
-    Displays GetDisplays();
-    pdx::Status<int64_t> GetVsyncTime(hwc2_display_t display);
-
-   private:
-    struct DisplayInfo {
-      hwc2_display_t id = 0;
-      pdx::LocalHandle driver_vsync_event_fd;
-      int64_t callback_vsync_timestamp{0};
-    };
-
-    DisplayInfo* GetDisplayInfo(hwc2_display_t display);
-
-    std::mutex mutex_;
-
-    bool got_first_hotplug_ = false;
-    DisplayInfo primary_display_;
-    std::optional<DisplayInfo> external_display_;
-    bool external_display_was_hotplugged_ = false;
-    sp<VsyncService> vsync_service_;
-  };
-
-  HWC::Error Validate(hwc2_display_t display);
-  HWC::Error Present(hwc2_display_t display);
-
-  void PostLayers(hwc2_display_t display);
-  void PostThread();
-
-  // The post thread has two controlling states:
-  // 1. Idle: no work to do (no visible surfaces).
-  // 2. Suspended: explicitly halted (system is not in VR mode).
-  // When either #1 or #2 is true then the post thread is quiescent, otherwise
-  // it is active.
-  using PostThreadStateType = uint32_t;
-  struct PostThreadState {
-    enum : PostThreadStateType {
-      Active = 0,
-      Idle = (1 << 0),
-      Suspended = (1 << 1),
-      Quit = (1 << 2),
-    };
-  };
-
-  void UpdatePostThreadState(uint32_t state, bool suspend);
-
-  // Blocks until either event_fd becomes readable, or we're interrupted by a
-  // control thread, or timeout_ms is reached before any events occur. Any
-  // errors are returned as negative errno values, with -ETIMEDOUT returned in
-  // the case of a timeout. If we're interrupted, kPostThreadInterrupted will be
-  // returned.
-  int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
-                                  int requested_events, int timeout_ms);
-
-  // WaitForPredictedVSync and SleepUntil are blocking calls made on the post
-  // thread that can be interrupted by a control thread. If interrupted, these
-  // calls return kPostThreadInterrupted.
-  int ReadWaitPPState();
-  pdx::Status<int64_t> WaitForPredictedVSync();
-  int SleepUntil(int64_t wakeup_timestamp);
-
-  // Initialize any newly connected displays, and set target_display_ to the
-  // display we should render to. Returns true if target_display_
-  // changed. Called only from the post thread.
-  bool UpdateTargetDisplay();
-
-  // Reconfigures the layer stack if the display surfaces changed since the last
-  // frame. Called only from the post thread.
-  void UpdateLayerConfig();
-
-  // Called on the post thread to create the Composer instance.
-  void CreateComposer();
-
-  // Called on the post thread when the post thread is resumed.
-  void OnPostThreadResumed();
-  // Called on the post thread when the post thread is paused or quits.
-  void OnPostThreadPaused();
-
-  // Use post_thread_wait_ to wait for a specific condition, specified by pred.
-  // timeout_sec < 0 means wait indefinitely, otherwise it specifies the timeout
-  // in seconds.
-  // The lock must be held when this function is called.
-  // Returns true if the wait was interrupted because the post thread was asked
-  // to quit.
-  bool PostThreadCondWait(std::unique_lock<std::mutex>& lock,
-                          int timeout_sec,
-                          const std::function<bool()>& pred);
-
-  // Map the given shared memory buffer to our broadcast ring to track updates
-  // to the config parameters.
-  int MapConfigBuffer(IonBuffer& ion_buffer);
-  void ConfigBufferDeleted();
-  // Poll for config udpates.
-  void UpdateConfigBuffer();
-
-  bool initialized_;
-  bool is_standalone_device_;
-
-  std::unique_ptr<Hwc2::Composer> composer_;
-  sp<ComposerCallback> composer_callback_;
-  RequestDisplayCallback request_display_callback_;
-
-  DisplayParams primary_display_;
-  std::optional<DisplayParams> external_display_;
-  DisplayParams* target_display_ = &primary_display_;
-
-  // The list of surfaces we should draw. Set by the display service when
-  // DirectSurfaces are added, removed, or change visibility. Written by the
-  // message dispatch thread and read by the post thread.
-  std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces_;
-  // Set to true by the dispatch thread whenever surfaces_ changes. Set to false
-  // by the post thread when the new list of surfaces is processed.
-  bool surfaces_changed_ = false;
-
-  std::vector<std::shared_ptr<DirectDisplaySurface>> current_surfaces_;
-
-  // Layer set for handling buffer flow into hardware composer layers. This
-  // vector must be sorted by surface_id in ascending order.
-  std::vector<Layer> layers_;
-
-  // The layer posting thread. This thread wakes up a short time before vsync to
-  // hand buffers to hardware composer.
-  std::thread post_thread_;
-
-  // Post thread state machine and synchronization primitives.
-  PostThreadStateType post_thread_state_{PostThreadState::Idle |
-                                         PostThreadState::Suspended};
-  std::atomic<bool> post_thread_quiescent_{true};
-  bool post_thread_resumed_{false};
-  pdx::LocalHandle post_thread_event_fd_;
-  std::mutex post_thread_mutex_;
-  std::condition_variable post_thread_wait_;
-  std::condition_variable post_thread_ready_;
-
-  // When boot is finished this will be set to true and the post thread will be
-  // notified via post_thread_wait_.
-  bool boot_finished_ = false;
-
-  // VSync sleep timerfd.
-  pdx::LocalHandle vsync_sleep_timer_fd_;
-
-  // The timestamp of the last vsync.
-  int64_t last_vsync_timestamp_ = 0;
-
-  // The number of vsync intervals to predict since the last vsync.
-  int vsync_prediction_interval_ = 1;
-
-  // Vsync count since display on.
-  uint32_t vsync_count_ = 0;
-
-  // Counter tracking the number of skipped frames.
-  int frame_skip_count_ = 0;
-
-  // Fd array for tracking retire fences that are returned by hwc. This allows
-  // us to detect when the display driver begins queuing frames.
-  std::vector<pdx::LocalHandle> retire_fence_fds_;
-
-  // If we are publishing vsync data, we will put it here.
-  std::unique_ptr<CPUMappedBroadcastRing<DvrVsyncRing>> vsync_ring_;
-
-  // Broadcast ring for receiving config data from the DisplayManager.
-  DvrConfigRing shared_config_ring_;
-  uint32_t shared_config_ring_sequence_{0};
-  // Config buffer for reading from the post thread.
-  DvrConfig post_thread_config_;
-  std::mutex shared_config_mutex_;
-
-  bool vsync_trace_parity_ = false;
-  sp<VsyncService> vsync_service_;
-
-  // Edid section.
-  void UpdateEdidData(Hwc2::Composer* composer, hwc2_display_t hw_id);
-  DisplayIdentificationData display_identification_data_;
-  uint8_t display_port_;
-
-  static constexpr int kPostThreadInterrupted = 1;
-
-  HardwareComposer(const HardwareComposer&) = delete;
-  void operator=(const HardwareComposer&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
diff --git a/libs/vr/libvrflinger/hwc_types.h b/libs/vr/libvrflinger/hwc_types.h
deleted file mode 100644
index 8b5c3b3..0000000
--- a/libs/vr/libvrflinger/hwc_types.h
+++ /dev/null
@@ -1,307 +0,0 @@
-#ifndef ANDROID_LIBVRFLINGER_HWCTYPES_H
-#define ANDROID_LIBVRFLINGER_HWCTYPES_H
-
-// General HWC type support. Hardware composer type support is a bit of a mess
-// between HWC1, HWC2 C/C++11, and HIDL types. Particularly bothersome is the
-// use of enum classes, which make analogous types between versions much
-// harder to deal with in a uniform way.
-//
-// These utilities help address some of these pains by providing a type-safe,
-// flexible interface to translate between different type spaces.
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include <string>
-#include <type_traits>
-
-namespace HWC {
-
-// Value types derived from HWC HAL types. Some of these are stand-alone,
-// while others are also wrapped in translator classes below.
-using ColorMode = int32_t;  // android_color_mode_t;
-using Config = hwc2_config_t;
-using ColorTransform =
-    std::underlying_type<android_color_transform_t>::type;          // int32_t;
-using Dataspace = std::underlying_type<android_dataspace_t>::type;  // int32_t;
-using DisplayId = hwc2_display_t;
-using DisplayRequest = std::underlying_type<HWC2::DisplayRequest>::type;
-using Hdr = std::underlying_type<android_hdr_t>::type;  // int32_t;
-using Layer = hwc2_layer_t;
-using PixelFormat =
-    std::underlying_type<android_pixel_format_t>::type;  // int32_t;
-
-// Type traits and casting utilities.
-
-// SFINAE utility to evaluate type expressions.
-template <typename...>
-using TestTypeExpression = void;
-
-// Traits type to determine the underlying type of an enum, integer,
-// or wrapper class.
-template <typename T, typename = typename std::is_enum<T>::type,
-          typename = typename std::is_integral<T>::type, typename = void>
-struct UnderlyingType {
-  using Type = T;
-};
-// Partial specialization that matches enum types. Captures the underlying type
-// of the enum in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::true_type, std::false_type> {
-  using Type = typename std::underlying_type<T>::type;
-};
-// Partial specialization that matches integral types. Captures the type of the
-// integer in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::false_type, std::true_type> {
-  using Type = T;
-};
-// Partial specialization that matches the wrapper types below. Captures
-// wrapper member type ValueType in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::false_type, std::false_type,
-                      TestTypeExpression<typename T::ValueType>> {
-  using Type = typename T::ValueType;
-};
-
-// Enable if T is an enum with underlying type U.
-template <typename T, typename U, typename ReturnType = void>
-using EnableIfMatchingEnum = typename std::enable_if<
-    std::is_enum<T>::value &&
-        std::is_same<U, typename UnderlyingType<T>::Type>::value,
-    ReturnType>::type;
-
-// Enable if T and U are the same size/alignment and have the same underlying
-// type. Handles enum, integral, and wrapper classes below.
-template <typename T, typename U, typename Return = void>
-using EnableIfSafeCast = typename std::enable_if<
-    sizeof(T) == sizeof(U) && alignof(T) == alignof(U) &&
-        std::is_same<typename UnderlyingType<T>::Type,
-                     typename UnderlyingType<U>::Type>::value,
-    Return>::type;
-
-// Safely cast between std::vectors of matching enum/integer/wraper types.
-// Normally this is not possible with pendantic compiler type checks. However,
-// given the same size, alignment, and underlying type this is safe due to
-// allocator requirements and array-like element access guarantees.
-template <typename T, typename U>
-EnableIfSafeCast<T, U, std::vector<T>*> VectorCast(std::vector<U>* in) {
-  return reinterpret_cast<std::vector<T>*>(in);
-}
-
-// Translator classes that wrap specific HWC types to make translating
-// between different types (especially enum class) in code cleaner.
-
-// Base type for the enum wrappers below. This type provides type definitions
-// and implicit conversion logic common to each wrapper type.
-template <typename EnumType>
-struct Wrapper {
-  // Alias type of this instantiantion of Wrapper. Useful for inheriting
-  // constructors in subclasses via "using Base::Base;" statements.
-  using Base = Wrapper<EnumType>;
-
-  // The enum type wrapped by this instantiation of Wrapper.
-  using BaseType = EnumType;
-
-  // The underlying type of the base enum type.
-  using ValueType = typename UnderlyingType<BaseType>::Type;
-
-  // A default constructor is not defined here. Subclasses should define one
-  // as appropriate to define the correct inital value for the enum type.
-
-  // Default copy constructor.
-  Wrapper(const Wrapper&) = default;
-
-  // Implicit conversion from ValueType.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  Wrapper(ValueType value) : value(value) {}
-
-  // Implicit conversion from BaseType.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  Wrapper(BaseType value) : value(static_cast<ValueType>(value)) {}
-
-  // Implicit conversion from an enum type of the same underlying type.
-  template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  Wrapper(const T& value) : value(static_cast<ValueType>(value)) {}
-
-  // Implicit conversion to BaseType.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  operator BaseType() const { return static_cast<BaseType>(value); }
-
-  // Implicit conversion to ValueType.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  operator ValueType() const { return value; }
-
-  template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
-  T cast() const {
-    return static_cast<T>(value);
-  }
-
-  // Converts to string using HWC2 stringification of BaseType.
-  std::string to_string() const {
-    return HWC2::to_string(static_cast<BaseType>(value));
-  }
-
-  bool operator!=(const Wrapper& other) const { return value != other.value; }
-  bool operator!=(ValueType other_value) const { return value != other_value; }
-  bool operator!=(BaseType other_value) const {
-    return static_cast<BaseType>(value) != other_value;
-  }
-  bool operator==(const Wrapper& other) const { return value == other.value; }
-  bool operator==(ValueType other_value) const { return value == other_value; }
-  bool operator==(BaseType other_value) const {
-    return static_cast<BaseType>(value) == other_value;
-  }
-
-  ValueType value;
-};
-
-struct Attribute final : public Wrapper<HWC2::Attribute> {
-  enum : ValueType {
-    Invalid = HWC2_ATTRIBUTE_INVALID,
-    Width = HWC2_ATTRIBUTE_WIDTH,
-    Height = HWC2_ATTRIBUTE_HEIGHT,
-    VsyncPeriod = HWC2_ATTRIBUTE_VSYNC_PERIOD,
-    DpiX = HWC2_ATTRIBUTE_DPI_X,
-    DpiY = HWC2_ATTRIBUTE_DPI_Y,
-  };
-
-  Attribute() : Base(Invalid) {}
-  using Base::Base;
-};
-
-struct BlendMode final : public Wrapper<HWC2::BlendMode> {
-  enum : ValueType {
-    Invalid = HWC2_BLEND_MODE_INVALID,
-    None = HWC2_BLEND_MODE_NONE,
-    Premultiplied = HWC2_BLEND_MODE_PREMULTIPLIED,
-    Coverage = HWC2_BLEND_MODE_COVERAGE,
-  };
-
-  BlendMode() : Base(Invalid) {}
-  using Base::Base;
-};
-
-struct Composition final : public Wrapper<HWC2::Composition> {
-  enum : ValueType {
-    Invalid = HWC2_COMPOSITION_INVALID,
-    Client = HWC2_COMPOSITION_CLIENT,
-    Device = HWC2_COMPOSITION_DEVICE,
-    SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
-    Cursor = HWC2_COMPOSITION_CURSOR,
-    Sideband = HWC2_COMPOSITION_SIDEBAND,
-  };
-
-  Composition() : Base(Invalid) {}
-  using Base::Base;
-};
-
-struct DisplayType final : public Wrapper<HWC2::DisplayType> {
-  enum : ValueType {
-    Invalid = HWC2_DISPLAY_TYPE_INVALID,
-    Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
-    Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
-  };
-
-  DisplayType() : Base(Invalid) {}
-  using Base::Base;
-};
-
-struct Error final : public Wrapper<HWC2::Error> {
-  enum : ValueType {
-    None = HWC2_ERROR_NONE,
-    BadConfig = HWC2_ERROR_BAD_CONFIG,
-    BadDisplay = HWC2_ERROR_BAD_DISPLAY,
-    BadLayer = HWC2_ERROR_BAD_LAYER,
-    BadParameter = HWC2_ERROR_BAD_PARAMETER,
-    HasChanges = HWC2_ERROR_HAS_CHANGES,
-    NoResources = HWC2_ERROR_NO_RESOURCES,
-    NotValidated = HWC2_ERROR_NOT_VALIDATED,
-    Unsupported = HWC2_ERROR_UNSUPPORTED,
-  };
-
-  Error() : Base(None) {}
-  using Base::Base;
-};
-
-struct LayerRequest final : public Wrapper<HWC2::LayerRequest> {
-  enum : ValueType {
-    ClearClientTarget = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET,
-  };
-
-  LayerRequest() : Base(0) {}
-  using Base::Base;
-};
-
-struct PowerMode final : public Wrapper<HWC2::PowerMode> {
-  enum : ValueType {
-    Off = HWC2_POWER_MODE_OFF,
-    DozeSuspend = HWC2_POWER_MODE_DOZE_SUSPEND,
-    Doze = HWC2_POWER_MODE_DOZE,
-    On = HWC2_POWER_MODE_ON,
-  };
-
-  PowerMode() : Base(Off) {}
-  using Base::Base;
-};
-
-struct Transform final : public Wrapper<HWC2::Transform> {
-  enum : ValueType {
-    None = 0,
-    FlipH = HWC_TRANSFORM_FLIP_H,
-    FlipV = HWC_TRANSFORM_FLIP_V,
-    Rotate90 = HWC_TRANSFORM_ROT_90,
-    Rotate180 = HWC_TRANSFORM_ROT_180,
-    Rotate270 = HWC_TRANSFORM_ROT_270,
-    FlipHRotate90 = HWC_TRANSFORM_FLIP_H_ROT_90,
-    FlipVRotate90 = HWC_TRANSFORM_FLIP_V_ROT_90,
-  };
-
-  Transform() : Base(None) {}
-  using Base::Base;
-};
-
-struct Vsync final : public Wrapper<HWC2::Vsync> {
-  enum : ValueType {
-    Invalid = HWC2_VSYNC_INVALID,
-    Enable = HWC2_VSYNC_ENABLE,
-    Disable = HWC2_VSYNC_DISABLE,
-  };
-
-  Vsync() : Base(Invalid) {}
-  using Base::Base;
-};
-
-// Utility color type.
-struct Color final {
-  Color(const Color&) = default;
-  Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {}
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  Color(hwc_color_t color) : r(color.r), g(color.g), b(color.b), a(color.a) {}
-
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  operator hwc_color_t() const { return {r, g, b, a}; }
-
-  uint8_t r __attribute__((aligned(1)));
-  uint8_t g __attribute__((aligned(1)));
-  uint8_t b __attribute__((aligned(1)));
-  uint8_t a __attribute__((aligned(1)));
-};
-
-// Utility rectangle type.
-struct Rect final {
-  // TODO(eieio): Implicit conversion to/from Android rect types.
-
-  int32_t left __attribute__((aligned(4)));
-  int32_t top __attribute__((aligned(4)));
-  int32_t right __attribute__((aligned(4)));
-  int32_t bottom __attribute__((aligned(4)));
-};
-
-}  // namespace HWC
-
-#endif  // ANDROID_LIBVRFLINGER_HWCTYPES_H
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
deleted file mode 100644
index ae52076..0000000
--- a/libs/vr/libvrflinger/include/dvr/vr_flinger.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef ANDROID_DVR_VR_FLINGER_H_
-#define ANDROID_DVR_VR_FLINGER_H_
-
-#include <thread>
-#include <memory>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include <pdx/service_dispatcher.h>
-#include <vr/vr_manager/vr_manager.h>
-
-namespace android {
-
-namespace Hwc2 {
-class Composer;
-}  // namespace Hwc2
-
-namespace dvr {
-
-class DisplayService;
-
-class VrFlinger {
- public:
-  using RequestDisplayCallback = std::function<void(bool)>;
-  static std::unique_ptr<VrFlinger> Create(
-      Hwc2::Composer* hidl,
-      hwc2_display_t primary_display_id,
-      RequestDisplayCallback request_display_callback);
-  ~VrFlinger();
-
-  // These functions are all called on surface flinger's main thread.
-  void OnBootFinished();
-  void GrantDisplayOwnership();
-  void SeizeDisplayOwnership();
-
-  // dump all vr flinger state.
-  std::string Dump();
-
- private:
-  VrFlinger();
-  bool Init(Hwc2::Composer* hidl,
-            hwc2_display_t primary_display_id,
-            RequestDisplayCallback request_display_callback);
-
-  // Needs to be a separate class for binder's ref counting
-  class PersistentVrStateCallback : public BnPersistentVrStateCallbacks {
-   public:
-    explicit PersistentVrStateCallback(
-        RequestDisplayCallback request_display_callback)
-        : request_display_callback_(request_display_callback) {}
-    void onPersistentVrStateChanged(bool enabled) override;
-   private:
-    RequestDisplayCallback request_display_callback_;
-  };
-
-  std::thread dispatcher_thread_;
-  std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher_;
-  std::shared_ptr<android::dvr::DisplayService> display_service_;
-  sp<PersistentVrStateCallback> persistent_vr_state_callback_;
-  RequestDisplayCallback request_display_callback_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_VR_FLINGER_H_
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
deleted file mode 100644
index 095f556..0000000
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ /dev/null
@@ -1,47 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-shared_libs = [
-    "android.hardware.configstore-utils",
-    "android.hardware.configstore@1.0",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "libgui",
-    "libhidlbase",
-    "liblog",
-    "libui",
-    "libutils",
-    "libnativewindow",
-    "libpdx_default_transport",
-    "libSurfaceFlingerProp",
-]
-
-static_libs = [
-    "libdisplay",
-]
-
-cc_test {
-    srcs: ["vrflinger_test.cpp"],
-    // See go/apct-presubmit for documentation on how this .filter file is used
-    // by Android's automated testing infrastructure for test filtering.
-    data: ["vrflinger_test.filter"],
-    static_libs: static_libs,
-    shared_libs: shared_libs,
-    cflags: [
-        "-DLOG_TAG=\"VrFlingerTest\"",
-        "-DTRACE=0",
-        "-O0",
-        "-g",
-        "-Wall",
-        "-Werror",
-    ],
-    header_libs: ["libsurfaceflinger_headers"],
-    name: "vrflinger_test",
-}
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
deleted file mode 100644
index ac44f74..0000000
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-#include <SurfaceFlingerProperties.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware_buffer.h>
-#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-#include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <gtest/gtest.h>
-#include <gui/ISurfaceComposer.h>
-#include <log/log.h>
-#include <utils/StrongPointer.h>
-
-#include <chrono>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <thread>
-
-#include <private/dvr/display_client.h>
-
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using android::dvr::display::DisplayClient;
-using android::dvr::display::Surface;
-using android::dvr::display::SurfaceAttribute;
-using android::dvr::display::SurfaceAttributeValue;
-
-namespace android {
-namespace dvr {
-
-// The transaction code for asking surface flinger if vr flinger is active. This
-// is done as a hidden api since it's only used for tests. See the "case 1028"
-// block in SurfaceFlinger::onTransact() in SurfaceFlinger.cpp.
-constexpr uint32_t kIsVrFlingerActiveTransactionCode = 1028;
-
-// The maximum amount of time to give vr flinger to activate/deactivate. If the
-// switch hasn't completed in this amount of time, the test will fail.
-constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1);
-
-// How long to wait between each check to see if the vr flinger switch
-// completed.
-constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
-
-// A Binder connection to surface flinger.
-class SurfaceFlingerConnection {
- public:
-  static std::unique_ptr<SurfaceFlingerConnection> Create() {
-    sp<ISurfaceComposer> surface_flinger = interface_cast<ISurfaceComposer>(
-        defaultServiceManager()->getService(String16("SurfaceFlinger")));
-    if (surface_flinger == nullptr) {
-      return nullptr;
-    }
-
-    return std::unique_ptr<SurfaceFlingerConnection>(
-        new SurfaceFlingerConnection(surface_flinger));
-  }
-
-  // Returns true if the surface flinger process is still running. We use this
-  // to detect if surface flinger has crashed.
-  bool IsAlive() {
-    IInterface::asBinder(surface_flinger_)->pingBinder();
-    return IInterface::asBinder(surface_flinger_)->isBinderAlive();
-  }
-
-  // Return true if vr flinger is currently active, false otherwise. If there's
-  // an error communicating with surface flinger, std::nullopt is returned.
-  std::optional<bool> IsVrFlingerActive() {
-    Parcel data, reply;
-    status_t result =
-        data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
-    if (result != OK) {
-      return std::nullopt;
-    }
-    result = IInterface::asBinder(surface_flinger_)
-                 ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
-    if (result != OK) {
-      return std::nullopt;
-    }
-    bool vr_flinger_active;
-    result = reply.readBool(&vr_flinger_active);
-    if (result != OK) {
-      return std::nullopt;
-    }
-    return vr_flinger_active;
-  }
-
-  enum class VrFlingerSwitchResult : int8_t {
-    kSuccess,
-    kTimedOut,
-    kCommunicationError,
-    kSurfaceFlingerDied
-  };
-
-  // Wait for vr flinger to become active or inactive.
-  VrFlingerSwitchResult WaitForVrFlinger(bool wait_active) {
-    return WaitForVrFlingerTimed(wait_active, kVrFlingerSwitchPollInterval,
-        kVrFlingerSwitchMaxTime);
-  }
-
-  // Wait for vr flinger to become active or inactive, specifying custom timeouts.
-  VrFlingerSwitchResult WaitForVrFlingerTimed(bool wait_active,
-      std::chrono::milliseconds pollInterval, std::chrono::seconds timeout) {
-    auto start_time = std::chrono::steady_clock::now();
-    while (1) {
-      std::this_thread::sleep_for(pollInterval);
-      if (!IsAlive()) {
-        return VrFlingerSwitchResult::kSurfaceFlingerDied;
-      }
-      std::optional<bool> vr_flinger_active = IsVrFlingerActive();
-      if (!vr_flinger_active.has_value()) {
-        return VrFlingerSwitchResult::kCommunicationError;
-      }
-      if (vr_flinger_active.value() == wait_active) {
-        return VrFlingerSwitchResult::kSuccess;
-      } else if (std::chrono::steady_clock::now() - start_time > timeout) {
-        return VrFlingerSwitchResult::kTimedOut;
-      }
-    }
-  }
-
- private:
-  SurfaceFlingerConnection(sp<ISurfaceComposer> surface_flinger)
-      : surface_flinger_(surface_flinger) {}
-
-  sp<ISurfaceComposer> surface_flinger_ = nullptr;
-};
-
-// This test activates vr flinger by creating a vr flinger surface, then
-// deactivates vr flinger by destroying the surface. We verify that vr flinger
-// is activated and deactivated as expected, and that surface flinger doesn't
-// crash.
-//
-// If the device doesn't support vr flinger (as repoted by ConfigStore), the
-// test does nothing.
-//
-// If the device is a standalone vr device, the test also does nothing, since
-// this test verifies the behavior of display handoff from surface flinger to vr
-// flinger and back, and standalone devices never hand control of the display
-// back to surface flinger.
-TEST(VrFlingerTest, ActivateDeactivate) {
-  android::ProcessState::self()->startThreadPool();
-
-  // Exit immediately if the device doesn't support vr flinger. This ConfigStore
-  // check is the same mechanism used by surface flinger to decide if it should
-  // initialize vr flinger.
-  bool vr_flinger_enabled = android::sysprop::use_vr_flinger(false);
-  if (!vr_flinger_enabled) {
-    return;
-  }
-
-  auto surface_flinger_connection = SurfaceFlingerConnection::Create();
-  ASSERT_NE(surface_flinger_connection, nullptr);
-
-  // Verify we start off with vr flinger disabled.
-  ASSERT_TRUE(surface_flinger_connection->IsAlive());
-  auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
-  ASSERT_TRUE(vr_flinger_active.has_value());
-  ASSERT_FALSE(vr_flinger_active.value());
-
-  // Create a vr flinger surface, and verify vr flinger becomes active.
-  // Introduce a scope so that, at the end of the scope, the vr flinger surface
-  // is destroyed, and vr flinger deactivates.
-  {
-    auto display_client = DisplayClient::Create();
-    ASSERT_NE(display_client, nullptr);
-    auto metrics = display_client->GetDisplayMetrics();
-    ASSERT_TRUE(metrics.ok());
-
-    auto surface = Surface::CreateSurface({
-        {SurfaceAttribute::Direct, SurfaceAttributeValue(true)},
-        {SurfaceAttribute::Visible, SurfaceAttributeValue(true)},
-    });
-    ASSERT_TRUE(surface.ok());
-    ASSERT_TRUE(surface.get() != nullptr);
-
-    auto queue = surface.get()->CreateQueue(
-        metrics.get().display_width, metrics.get().display_height,
-        /*layer_count=*/1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
-        AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
-            AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
-            AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
-        /*capacity=*/1,
-        /*metadata_size=*/0);
-    ASSERT_TRUE(queue.ok());
-    ASSERT_TRUE(queue.get() != nullptr);
-
-    size_t slot;
-    pdx::LocalHandle release_fence;
-    auto buffer = queue.get()->Dequeue(/*timeout=*/0, &slot, &release_fence);
-    ASSERT_TRUE(buffer.ok());
-    ASSERT_TRUE(buffer.get() != nullptr);
-
-    ASSERT_EQ(buffer.get()->width(), metrics.get().display_width);
-    ASSERT_EQ(buffer.get()->height(), metrics.get().display_height);
-
-    void* raw_buf = nullptr;
-    ASSERT_GE(buffer.get()->buffer()->Lock(
-                  AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, /*x=*/0, /*y=*/0,
-                  buffer.get()->width(), buffer.get()->height(), &raw_buf),
-              0);
-    ASSERT_NE(raw_buf, nullptr);
-    uint32_t* pixels = static_cast<uint32_t*>(raw_buf);
-
-    for (int i = 0; i < buffer.get()->stride() * buffer.get()->height(); ++i) {
-      pixels[i] = 0x0000ff00;
-    }
-
-    ASSERT_GE(buffer.get()->buffer()->Unlock(), 0);
-
-    ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 0);
-
-    ASSERT_EQ(
-        surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/true),
-        SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
-  }
-
-  // Now that the vr flinger surface is destroyed, vr flinger should deactivate.
-  ASSERT_EQ(
-      surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/false),
-      SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.filter b/libs/vr/libvrflinger/tests/vrflinger_test.filter
deleted file mode 100644
index 030bb7b..0000000
--- a/libs/vr/libvrflinger/tests/vrflinger_test.filter
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-        "presubmit": {
-            "filter": "BootVrFlingerTest.*"
-        }
-}
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
deleted file mode 100644
index a8a8476..0000000
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-#include <dvr/vr_flinger.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <memory>
-
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <private/dvr/display_client.h>
-#include <processgroup/sched_policy.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
-
-#include <functional>
-
-#include "DisplayHardware/ComposerHal.h"
-#include "display_manager_service.h"
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-std::unique_ptr<VrFlinger> VrFlinger::Create(
-    Hwc2::Composer* hidl, hwc2_display_t primary_display_id,
-    RequestDisplayCallback request_display_callback) {
-  std::unique_ptr<VrFlinger> vr_flinger(new VrFlinger);
-  if (vr_flinger->Init(hidl, primary_display_id, request_display_callback))
-    return vr_flinger;
-  else
-    return nullptr;
-}
-
-VrFlinger::VrFlinger() {}
-
-VrFlinger::~VrFlinger() {
-  if (persistent_vr_state_callback_.get()) {
-    sp<IVrManager> vr_manager = interface_cast<IVrManager>(
-        defaultServiceManager()->checkService(String16("vrmanager")));
-    if (vr_manager.get()) {
-      vr_manager->unregisterPersistentVrStateListener(
-          persistent_vr_state_callback_);
-    }
-  }
-
-  if (dispatcher_)
-    dispatcher_->SetCanceled(true);
-  if (dispatcher_thread_.joinable())
-    dispatcher_thread_.join();
-}
-
-bool VrFlinger::Init(Hwc2::Composer* hidl,
-                     hwc2_display_t primary_display_id,
-                     RequestDisplayCallback request_display_callback) {
-  if (!hidl || !request_display_callback)
-    return false;
-
-  std::shared_ptr<android::pdx::Service> service;
-
-  ALOGI("Starting up VrFlinger...");
-
-  // We need to be able to create endpoints with full perms.
-  umask(0000);
-
-  android::ProcessState::self()->startThreadPool();
-
-  request_display_callback_ = request_display_callback;
-
-  dispatcher_ = android::pdx::ServiceDispatcher::Create();
-  CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher.");
-
-  display_service_ = android::dvr::DisplayService::Create(
-      hidl, primary_display_id, request_display_callback);
-  CHECK_ERROR(!display_service_, error, "Failed to create display service.");
-  dispatcher_->AddService(display_service_);
-
-  service = android::dvr::DisplayManagerService::Create(display_service_);
-  CHECK_ERROR(!service, error, "Failed to create display manager service.");
-  dispatcher_->AddService(service);
-
-  dispatcher_thread_ = std::thread([this]() {
-    prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
-    ALOGI("Entering message loop.");
-
-    setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
-    set_sched_policy(0, SP_FOREGROUND);
-
-    int ret = dispatcher_->EnterDispatchLoop();
-    if (ret < 0) {
-      ALOGE("Dispatch loop exited because: %s\n", strerror(-ret));
-    }
-  });
-
-  return true;
-
-error:
-  return false;
-}
-
-void VrFlinger::OnBootFinished() {
-  display_service_->OnBootFinished();
-  sp<IVrManager> vr_manager = interface_cast<IVrManager>(
-      defaultServiceManager()->checkService(String16("vrmanager")));
-  if (vr_manager.get()) {
-    persistent_vr_state_callback_ =
-        new PersistentVrStateCallback(request_display_callback_);
-    vr_manager->registerPersistentVrStateListener(
-        persistent_vr_state_callback_);
-  } else {
-    ALOGE("Unable to register vr flinger for persistent vr mode changes");
-  }
-}
-
-void VrFlinger::GrantDisplayOwnership() {
-  display_service_->GrantDisplayOwnership();
-}
-
-void VrFlinger::SeizeDisplayOwnership() {
-  display_service_->SeizeDisplayOwnership();
-}
-
-std::string VrFlinger::Dump() {
-  // TODO(karthikrs): Add more state information here.
-  return display_service_->DumpState(0/*unused*/);
-}
-
-void VrFlinger::PersistentVrStateCallback::onPersistentVrStateChanged(
-    bool enabled) {
-  ALOGV("Notified persistent vr mode is %s", enabled ? "on" : "off");
-  // TODO(eieio): Determine the correct signal to request display control.
-  // Persistent VR mode is not enough.
-  // request_display_callback_(enabled);
-}
-}  // namespace dvr
-}  // namespace android
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index de36a7a..f4dbe49 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -29,6 +29,7 @@
 #include <private/android/AHardwareBufferHelpers.h>
 #include <stdlib.h>
 #include <string.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 
 #include <condition_variable>
 #include <deque>
@@ -564,9 +565,11 @@
     newList.push_back(EGL_NONE);
 }
 
+using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+
 // Gets the native pixel format corrsponding to the passed EGLConfig.
 void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
-                          android_pixel_format* format) {
+                          PixelFormat* format) {
     // Set the native window's buffers format to match what this config requests.
     // Whether to use sRGB gamma is not part of the EGLconfig, but is part
     // of our native format. So if sRGB gamma is requested, we have to
@@ -599,28 +602,30 @@
     //    strip colorspace from attribs.
     // endif
     if (a == 0) {
-        if (colorDepth <= 16) {
-            *format = HAL_PIXEL_FORMAT_RGB_565;
+        if (8 == r && 0 == g && 0 == b) {
+            *format = PixelFormat::R_8;
+        } else if (colorDepth <= 16) {
+            *format = PixelFormat::RGB_565;
         } else {
             if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
                 if (colorDepth > 24) {
-                    *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                    *format = PixelFormat::RGBA_1010102;
                 } else {
-                    *format = HAL_PIXEL_FORMAT_RGBX_8888;
+                    *format = PixelFormat::RGBX_8888;
                 }
             } else {
-                *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+                *format = PixelFormat::RGBA_FP16;
             }
         }
     } else {
         if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
             if (colorDepth > 24) {
-                *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                *format = PixelFormat::RGBA_1010102;
             } else {
-                *format = HAL_PIXEL_FORMAT_RGBA_8888;
+                *format = PixelFormat::RGBA_8888;
             }
         } else {
-            *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            *format = PixelFormat::RGBA_FP16;
         }
     }
 }
@@ -678,7 +683,7 @@
     }
 
     EGLDisplay iDpy = dp->disp.dpy;
-    android_pixel_format format;
+    PixelFormat format;
     getNativePixelFormat(iDpy, cnx, config, &format);
 
     // now select correct colorspace and dataspace based on user's attribute list
@@ -694,7 +699,7 @@
     attrib_list = strippedAttribList.data();
 
     if (!cnx->useAngle) {
-        int err = native_window_set_buffers_format(window, format);
+        int err = native_window_set_buffers_format(window, static_cast<int>(format));
         if (err != 0) {
             ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
             native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
@@ -818,7 +823,7 @@
     if (!dp) return EGL_NO_SURFACE;
 
     EGLDisplay iDpy = dp->disp.dpy;
-    android_pixel_format format;
+    PixelFormat format;
     getNativePixelFormat(iDpy, cnx, config, &format);
 
     // Select correct colorspace based on user's attribute list
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index d6fc695..d205231 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,7 +34,10 @@
     sp<IBinder> displayToken = nullptr;
     sp<SurfaceControl> surfaceControl = nullptr;
     if (it == mDisplays.end()) {
-        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+        if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+            displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+        }
+
         if (displayToken == nullptr) {
             ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
             return nullptr;
@@ -157,7 +160,11 @@
     HwDisplayConfig activeConfig;
     HwDisplayState  activeState;
 
-    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+    sp<IBinder> displayToken;
+    if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+    }
+
     if (displayToken == nullptr) {
         ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
     } else {
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 73e5749..ec58325 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -24,6 +24,7 @@
 
 cc_defaults {
     name: "inputflinger_defaults",
+    cpp_std: "c++20",
     cflags: [
         "-Wall",
         "-Wextra",
@@ -71,9 +72,7 @@
         "libstatssocket",
         "libutils",
         "libui",
-        "lib-platform-compat-native-api",
         "server_configurable_flags",
-        "InputFlingerProperties",
     ],
     static_libs: [
         "libattestation",
@@ -125,7 +124,7 @@
         "InputListener.cpp",
         "InputReaderBase.cpp",
         "InputThread.cpp",
-        "VibrationElement.cpp"
+        "VibrationElement.cpp",
     ],
 }
 
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index b612ca7..8300e8a 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -71,8 +71,7 @@
 
     void erase(const std::function<bool(const T&)>& lambda) {
         std::scoped_lock lock(mLock);
-        mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
-                [&lambda](const T& t) { return lambda(t); }), mQueue.end());
+        std::erase_if(mQueue, [&lambda](const auto& t) { return lambda(t); });
     }
 
     /**
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index a9cbd5a..19cad7b 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -68,7 +68,8 @@
 }
 
 static bool isTouchEvent(const NotifyMotionArgs& args) {
-    return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
+    return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) ||
+            isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN);
 }
 
 // --- ClassifierEvent ---
@@ -345,7 +346,7 @@
 
 // --- InputClassifier ---
 
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+InputClassifier::InputClassifier(InputListenerInterface& listener)
       : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
 
 void InputClassifier::setMotionClassifierEnabled(bool enabled) {
@@ -369,12 +370,12 @@
 
 void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
     // pass through
-    mListener->notifyConfigurationChanged(args);
+    mListener.notifyConfigurationChanged(args);
 }
 
 void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
     // pass through
-    mListener->notifyKey(args);
+    mListener.notifyKey(args);
 }
 
 void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
@@ -382,28 +383,28 @@
     // MotionClassifier is only used for touch events, for now
     const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
     if (!sendToMotionClassifier) {
-        mListener->notifyMotion(args);
+        mListener.notifyMotion(args);
         return;
     }
 
     NotifyMotionArgs newArgs(*args);
     newArgs.classification = mMotionClassifier->classify(newArgs);
-    mListener->notifyMotion(&newArgs);
+    mListener.notifyMotion(&newArgs);
 }
 
 void InputClassifier::notifySensor(const NotifySensorArgs* args) {
     // pass through
-    mListener->notifySensor(args);
+    mListener.notifySensor(args);
 }
 
 void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) {
     // pass through
-    mListener->notifyVibratorState(args);
+    mListener.notifyVibratorState(args);
 }
 
 void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
     // pass through
-    mListener->notifySwitch(args);
+    mListener.notifySwitch(args);
 }
 
 void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
@@ -412,12 +413,12 @@
         mMotionClassifier->reset(*args);
     }
     // continue to next stage
-    mListener->notifyDeviceReset(args);
+    mListener.notifyDeviceReset(args);
 }
 
 void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
     // pass through
-    mListener->notifyPointerCaptureChanged(args);
+    mListener.notifyPointerCaptureChanged(args);
 }
 
 void InputClassifier::setMotionClassifier(
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 1eef020..deeae7c 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -18,7 +18,6 @@
 #define _UI_INPUT_CLASSIFIER_H
 
 #include <android-base/thread_annotations.h>
-#include <utils/RefBase.h>
 #include <thread>
 #include <unordered_map>
 
@@ -88,7 +87,7 @@
  * Base interface for an InputListener stage.
  * Provides classification to events.
  */
-class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
+class InputClassifierInterface : public InputListenerInterface {
 public:
     virtual void setMotionClassifierEnabled(bool enabled) = 0;
     /**
@@ -96,7 +95,7 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual void dump(std::string& dump) = 0;
-protected:
+
     InputClassifierInterface() { }
     virtual ~InputClassifierInterface() { }
 };
@@ -223,7 +222,7 @@
  */
 class InputClassifier : public InputClassifierInterface {
 public:
-    explicit InputClassifier(const sp<InputListenerInterface>& listener);
+    explicit InputClassifier(InputListenerInterface& listener);
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
     virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -245,7 +244,7 @@
     // Protect access to mMotionClassifier, since it may become null via a hidl callback
     std::mutex mLock;
     // The next stage to pass input events to
-    sp<InputListenerInterface> mListener;
+    InputListenerInterface& mListener;
 
     std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
     std::thread mInitializeMotionClassifierThread;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 71b0f5f..158d0eb 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -44,8 +44,8 @@
     return id == rhs.id && eventTime == rhs.eventTime;
 }
 
-void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifyConfigurationChanged(this);
+void NotifyConfigurationChangedArgs::notify(InputListenerInterface& listener) const {
+    listener.notifyConfigurationChanged(this);
 }
 
 // --- NotifyKeyArgs ---
@@ -89,8 +89,8 @@
             downTime == rhs.downTime;
 }
 
-void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifyKey(this);
+void NotifyKeyArgs::notify(InputListenerInterface& listener) const {
+    listener.notifyKey(this);
 }
 
 // --- NotifyMotionArgs ---
@@ -188,8 +188,8 @@
     return true;
 }
 
-void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifyMotion(this);
+void NotifyMotionArgs::notify(InputListenerInterface& listener) const {
+    listener.notifyMotion(this);
 }
 
 // --- NotifySwitchArgs ---
@@ -212,8 +212,8 @@
             switchValues == rhs.switchValues && switchMask == rhs.switchMask;
 }
 
-void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifySwitch(this);
+void NotifySwitchArgs::notify(InputListenerInterface& listener) const {
+    listener.notifySwitch(this);
 }
 
 // --- NotifySensorArgs ---
@@ -247,8 +247,8 @@
             hwTimestamp == rhs.hwTimestamp && values == rhs.values;
 }
 
-void NotifySensorArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifySensor(this);
+void NotifySensorArgs::notify(InputListenerInterface& listener) const {
+    listener.notifySensor(this);
 }
 
 // --- NotifyVibratorStateArgs ---
@@ -265,8 +265,8 @@
             isOn == rhs.isOn;
 }
 
-void NotifyVibratorStateArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifyVibratorState(this);
+void NotifyVibratorStateArgs::notify(InputListenerInterface& listener) const {
+    listener.notifyVibratorState(this);
 }
 
 // --- NotifyDeviceResetArgs ---
@@ -281,8 +281,8 @@
     return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId;
 }
 
-void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifyDeviceReset(this);
+void NotifyDeviceResetArgs::notify(InputListenerInterface& listener) const {
+    listener.notifyDeviceReset(this);
 }
 
 // --- NotifyPointerCaptureChangedArgs ---
@@ -299,8 +299,8 @@
     return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
 }
 
-void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
-    listener->notifyPointerCaptureChanged(this);
+void NotifyPointerCaptureChangedArgs::notify(InputListenerInterface& listener) const {
+    listener.notifyPointerCaptureChanged(this);
 }
 
 // --- QueuedInputListener ---
@@ -312,9 +312,8 @@
     }
 }
 
-QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
-        mInnerListener(innerListener) {
-}
+QueuedInputListener::QueuedInputListener(InputListenerInterface& innerListener)
+      : mInnerListener(innerListener) {}
 
 QueuedInputListener::~QueuedInputListener() {
     size_t count = mArgsQueue.size();
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 7b3658d..221e193 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -58,8 +58,8 @@
         const sp<InputReaderPolicyInterface>& readerPolicy,
         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
     mDispatcher = createInputDispatcher(dispatcherPolicy);
-    mClassifier = new InputClassifier(mDispatcher);
-    mReader = createInputReader(readerPolicy, mClassifier);
+    mClassifier = std::make_unique<InputClassifier>(*mDispatcher);
+    mReader = createInputReader(readerPolicy, *mClassifier);
 }
 
 InputManager::~InputManager() {
@@ -102,16 +102,16 @@
     return status;
 }
 
-sp<InputReaderInterface> InputManager::getReader() {
-    return mReader;
+InputReaderInterface& InputManager::getReader() {
+    return *mReader;
 }
 
-sp<InputClassifierInterface> InputManager::getClassifier() {
-    return mClassifier;
+InputClassifierInterface& InputManager::getClassifier() {
+    return *mClassifier;
 }
 
-sp<InputDispatcherInterface> InputManager::getDispatcher() {
-    return mDispatcher;
+InputDispatcherInterface& InputManager::getDispatcher() {
+    return *mDispatcher;
 }
 
 // Used by tests only.
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 4c07c22..a6baf2f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -30,7 +30,6 @@
 #include <input/InputTransport.h>
 
 #include <android/os/BnInputFlinger.h>
-#include <android/os/IInputFlinger.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -76,13 +75,13 @@
     virtual status_t stop() = 0;
 
     /* Gets the input reader. */
-    virtual sp<InputReaderInterface> getReader() = 0;
+    virtual InputReaderInterface& getReader() = 0;
 
     /* Gets the input classifier */
-    virtual sp<InputClassifierInterface> getClassifier() = 0;
+    virtual InputClassifierInterface& getClassifier() = 0;
 
     /* Gets the input dispatcher. */
-    virtual sp<InputDispatcherInterface> getDispatcher() = 0;
+    virtual InputDispatcherInterface& getDispatcher() = 0;
 };
 
 class InputManager : public InputManagerInterface, public BnInputFlinger {
@@ -97,9 +96,9 @@
     status_t start() override;
     status_t stop() override;
 
-    sp<InputReaderInterface> getReader() override;
-    sp<InputClassifierInterface> getClassifier() override;
-    sp<InputDispatcherInterface> getDispatcher() override;
+    InputReaderInterface& getReader() override;
+    InputClassifierInterface& getClassifier() override;
+    InputDispatcherInterface& getDispatcher() override;
 
     status_t dump(int fd, const Vector<String16>& args) override;
     binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
@@ -107,11 +106,11 @@
     binder::Status setFocusedWindow(const gui::FocusRequest&) override;
 
 private:
-    sp<InputReaderInterface> mReader;
+    std::unique_ptr<InputReaderInterface> mReader;
 
-    sp<InputClassifierInterface> mClassifier;
+    std::unique_ptr<InputClassifierInterface> mClassifier;
 
-    sp<InputDispatcherInterface> mDispatcher;
+    std::unique_ptr<InputDispatcherInterface> mDispatcher;
 };
 
 } // namespace android
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 05ef489..a864cf8 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,12 +19,12 @@
 //#define LOG_NDEBUG 0
 
 #include "InputReaderBase.h"
-#include <ftl/NamedEnum.h>
 #include "input/DisplayViewport.h"
 #include "input/Input.h"
 
-#include <android/log.h>
 #include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <ftl/enum.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -117,7 +117,7 @@
     }
     if (count > 1) {
         ALOGW("Found %zu viewports with type %s, but expected 1 at most", count,
-              NamedEnum::string(type).c_str());
+              ftl::enum_string(type).c_str());
     }
     return result;
 }
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 902bd0d..75071d5 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -28,7 +28,6 @@
         "libstatslog",
         "libui",
         "libutils",
-        "lib-platform-compat-native-api",
     ],
     static_libs: [
         "libattestation",
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 6ce0313..41e9ce2 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -21,6 +21,7 @@
 #include <gui/constants.h>
 #include "../dispatcher/InputDispatcher.h"
 
+using android::base::Result;
 using android::gui::WindowInfo;
 using android::gui::WindowInfoHandle;
 using android::os::IInputConstants;
@@ -162,15 +163,15 @@
     }
 
 protected:
-    explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
-          : mDispatcher(dispatcher) {
-        mClientChannel = *mDispatcher->createInputChannel(name);
+    explicit FakeInputReceiver(InputDispatcher& dispatcher, const std::string name) {
+        Result<std::unique_ptr<InputChannel>> channelResult = dispatcher.createInputChannel(name);
+        LOG_ALWAYS_FATAL_IF(!channelResult.ok());
+        mClientChannel = std::move(*channelResult);
         mConsumer = std::make_unique<InputConsumer>(mClientChannel);
     }
 
     virtual ~FakeInputReceiver() {}
 
-    sp<InputDispatcher> mDispatcher;
     std::shared_ptr<InputChannel> mClientChannel;
     std::unique_ptr<InputConsumer> mConsumer;
     PreallocatedInputEventFactory mEventFactory;
@@ -182,7 +183,7 @@
     static const int32_t HEIGHT = 200;
 
     FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
-                     const sp<InputDispatcher>& dispatcher, const std::string name)
+                     InputDispatcher& dispatcher, const std::string name)
           : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
         inputApplicationHandle->updateInfo();
         updateInfo();
@@ -236,8 +237,8 @@
                      /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
                      identityTransform, /* xPrecision */ 0,
                      /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, currentTime, currentTime,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, currentTime,
+                     currentTime,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     return event;
 }
@@ -272,13 +273,13 @@
 static void benchmarkNotifyMotion(benchmark::State& state) {
     // Create dispatcher
     sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
-    sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+    std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
     dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
     dispatcher->start();
 
     // Create a window that will receive motion events
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window");
 
     dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
@@ -306,13 +307,13 @@
 static void benchmarkInjectMotion(benchmark::State& state) {
     // Create dispatcher
     sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
-    sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+    std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
     dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
     dispatcher->start();
 
     // Create a window that will receive motion events
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window");
 
     dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
 
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 171f2b5..cdad9c9 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -66,9 +66,7 @@
         "libui",
         "libgui",
         "libutils",
-        "lib-platform-compat-native-api",
         "server_configurable_flags",
-        "InputFlingerProperties",
     ],
     static_libs: [
         "libattestation",
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index cee9c39..b4497fd 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -22,7 +22,7 @@
 
 Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
                        const IdGenerator& idGenerator)
-      : status(STATUS_NORMAL),
+      : status(Status::NORMAL),
         inputChannel(inputChannel),
         monitor(monitor),
         inputPublisher(inputChannel),
@@ -40,19 +40,6 @@
     return "?";
 }
 
-const char* Connection::getStatusLabel() const {
-    switch (status) {
-        case STATUS_NORMAL:
-            return "NORMAL";
-        case STATUS_BROKEN:
-            return "BROKEN";
-        case STATUS_ZOMBIE:
-            return "ZOMBIE";
-        default:
-            return "UNKNOWN";
-    }
-}
-
 std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
     for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
         if ((*it)->seq == seq) {
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index c4262ad..dc6a081 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -20,6 +20,7 @@
 #include "InputState.h"
 
 #include <input/InputTransport.h>
+#include <utils/RefBase.h>
 #include <deque>
 
 namespace android::inputdispatcher {
@@ -32,13 +33,16 @@
     virtual ~Connection();
 
 public:
-    enum Status {
+    enum class Status {
         // Everything is peachy.
-        STATUS_NORMAL,
+        NORMAL,
         // An unrecoverable communication error has occurred.
-        STATUS_BROKEN,
+        BROKEN,
         // The input channel has been unregistered.
-        STATUS_ZOMBIE
+        ZOMBIE,
+
+        ftl_first = NORMAL,
+        ftl_last = ZOMBIE,
     };
 
     Status status;
@@ -65,7 +69,6 @@
     inline const std::string getInputChannelName() const { return inputChannel->getName(); }
 
     const std::string getWindowName() const;
-    const char* getStatusLabel() const;
 
     std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
 };
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index b3c5709..4636820 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -18,7 +18,7 @@
 #define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
 
 #include <gui/WindowInfo.h>
-#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
 #include <string>
 
 namespace android {
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 82b4fe4..8046bbe 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -32,25 +32,26 @@
     return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
              entry.displayId},
             entry.action,
-            entry.downTime,
             entry.flags & VERIFIED_KEY_EVENT_FLAGS,
+            entry.downTime,
             entry.keyCode,
             entry.scanCode,
             entry.metaState,
             entry.repeatCount};
 }
 
-VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) {
-    const float rawX = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
-    const float rawY = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry,
+                                                       const ui::Transform& rawTransform) {
+    const vec2 rawXY = MotionEvent::calculateTransformedXY(entry.source, rawTransform,
+                                                           entry.pointerCoords[0].getXYValue());
     const int actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK;
     return {{VerifiedInputEvent::Type::MOTION, entry.deviceId, entry.eventTime, entry.source,
              entry.displayId},
-            rawX,
-            rawY,
+            rawXY.x,
+            rawXY.y,
             actionMasked,
-            entry.downTime,
             entry.flags & VERIFIED_MOTION_EVENT_FLAGS,
+            entry.downTime,
             entry.metaState,
             entry.buttonState};
 }
@@ -174,12 +175,13 @@
     if (!GetBoolProperty("ro.debuggable", false)) {
         return "KeyEvent";
     }
-    return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
-                        ", source=0x%08x, displayId=%" PRId32 ", action=%s, "
-                        "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
+    return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32
+                        ", action=%s, "
+                        "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, "
                         "repeatCount=%d), policyFlags=0x%08x",
-                        deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
-                        flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+                        deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
+                        KeyEvent::actionToString(action), flags, KeyEvent::getLabel(keyCode),
+                        keyCode, scanCode, metaState, repeatCount, policyFlags);
 }
 
 void KeyEntry::recycle() {
@@ -191,6 +193,18 @@
     interceptKeyWakeupTime = 0;
 }
 
+// --- TouchModeEntry ---
+
+TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode)
+      : EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
+        inTouchMode(inTouchMode) {}
+
+TouchModeEntry::~TouchModeEntry() {}
+
+std::string TouchModeEntry::getDescription() const {
+    return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false");
+}
+
 // --- MotionEntry ---
 
 MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
@@ -200,7 +214,7 @@
                          int32_t edgeFlags, float xPrecision, float yPrecision,
                          float xCursorPosition, float yCursorPosition, nsecs_t downTime,
                          uint32_t pointerCount, const PointerProperties* pointerProperties,
-                         const PointerCoords* pointerCoords, float xOffset, float yOffset)
+                         const PointerCoords* pointerCoords)
       : EventEntry(id, Type::MOTION, eventTime, policyFlags),
         deviceId(deviceId),
         source(source),
@@ -221,9 +235,6 @@
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
         this->pointerCoords[i].copyFrom(pointerCoords[i]);
-        if (xOffset || yOffset) {
-            this->pointerCoords[i].applyOffset(xOffset, yOffset);
-        }
     }
 }
 
@@ -235,12 +246,12 @@
     }
     std::string msg;
     msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64
-                        ", source=0x%08x, displayId=%" PRId32
+                        ", source=%s, displayId=%" PRId32
                         ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
                         "buttonState=0x%08x, "
                         "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
                         "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
-                        deviceId, eventTime, source, displayId,
+                        deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
                         MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState,
                         buttonState, motionClassificationToString(classification), edgeFlags,
                         xPrecision, yPrecision, xCursorPosition, yCursorPosition);
@@ -275,9 +286,10 @@
 
 std::string SensorEntry::getDescription() const {
     std::string msg;
-    msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, "
+    msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, "
                         "accuracy=0x%08x, hwTimestamp=%" PRId64,
-                        deviceId, source, sensorType, accuracy, hwTimestamp);
+                        deviceId, inputEventSourceToString(source).c_str(),
+                        ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp);
 
     if (!GetBoolProperty("ro.debuggable", false)) {
         for (size_t i = 0; i < values.size(); i++) {
@@ -296,15 +308,14 @@
 volatile int32_t DispatchEntry::sNextSeqAtomic;
 
 DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
-                             ui::Transform transform, float globalScaleFactor,
-                             uint32_t displayOrientation, int2 displaySize)
+                             const ui::Transform& transform, const ui::Transform& rawTransform,
+                             float globalScaleFactor)
       : seq(nextSeq()),
         eventEntry(std::move(eventEntry)),
         targetFlags(targetFlags),
         transform(transform),
+        rawTransform(rawTransform),
         globalScaleFactor(globalScaleFactor),
-        displayOrientation(displayOrientation),
-        displaySize(displaySize),
         deliveryTime(0),
         resolvedAction(0),
         resolvedFlags(0) {}
@@ -318,16 +329,4 @@
     return seq;
 }
 
-// --- CommandEntry ---
-
-CommandEntry::CommandEntry(Command command)
-      : command(command),
-        eventTime(0),
-        keyEntry(nullptr),
-        userActivityEventType(0),
-        seq(0),
-        handled(false) {}
-
-CommandEntry::~CommandEntry() {}
-
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ffe3bb6..0f79296 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -39,6 +39,9 @@
         SENSOR,
         POINTER_CAPTURE_CHANGED,
         DRAG,
+        TOUCH_MODE_CHANGED,
+
+        ftl_last = TOUCH_MODE_CHANGED
     };
 
     int32_t id;
@@ -181,11 +184,10 @@
                 int32_t metaState, int32_t buttonState, MotionClassification classification,
                 int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
                 float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
-                const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-                float xOffset, float yOffset);
+                const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
     std::string getDescription() const override;
 
-    virtual ~MotionEntry();
+    ~MotionEntry() override;
 };
 
 struct SensorEntry : EventEntry {
@@ -207,6 +209,15 @@
     ~SensorEntry() override;
 };
 
+struct TouchModeEntry : EventEntry {
+    bool inTouchMode;
+
+    TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode);
+    std::string getDescription() const override;
+
+    ~TouchModeEntry() override;
+};
+
 // Tracks the progress of dispatching a particular event to a particular connection.
 struct DispatchEntry {
     const uint32_t seq; // unique sequence number, never 0
@@ -214,9 +225,8 @@
     std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
     int32_t targetFlags;
     ui::Transform transform;
+    ui::Transform rawTransform;
     float globalScaleFactor;
-    uint32_t displayOrientation;
-    int2 displaySize;
     // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
     // and will be undefined before that.
     nsecs_t deliveryTime; // time when the event was actually delivered
@@ -229,8 +239,8 @@
     int32_t resolvedFlags;
 
     DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
-                  ui::Transform transform, float globalScaleFactor, uint32_t displayOrientation,
-                  int2 displaySize);
+                  const ui::Transform& transform, const ui::Transform& rawTransform,
+                  float globalScaleFactor);
 
     inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
 
@@ -243,56 +253,8 @@
 };
 
 VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
-VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry);
-
-class InputDispatcher;
-// A command entry captures state and behavior for an action to be performed in the
-// dispatch loop after the initial processing has taken place.  It is essentially
-// a kind of continuation used to postpone sensitive policy interactions to a point
-// in the dispatch loop where it is safe to release the lock (generally after finishing
-// the critical parts of the dispatch cycle).
-//
-// The special thing about commands is that they can voluntarily release and reacquire
-// the dispatcher lock at will.  Initially when the command starts running, the
-// dispatcher lock is held.  However, if the command needs to call into the policy to
-// do some work, it can release the lock, do the work, then reacquire the lock again
-// before returning.
-//
-// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
-// never calls into the policy while holding its lock.
-//
-// Commands are implicitly 'LockedInterruptible'.
-struct CommandEntry;
-typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
-
-class Connection;
-struct CommandEntry {
-    explicit CommandEntry(Command command);
-    ~CommandEntry();
-
-    Command command;
-
-    // parameters for the command (usage varies by command)
-    sp<Connection> connection;
-    nsecs_t eventTime;
-    std::shared_ptr<KeyEntry> keyEntry;
-    std::shared_ptr<SensorEntry> sensorEntry;
-    std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
-    std::string reason;
-    int32_t userActivityEventType;
-    uint32_t seq;
-    bool handled;
-    sp<IBinder> connectionToken;
-    sp<IBinder> oldToken;
-    sp<IBinder> newToken;
-    std::string obscuringPackage;
-    PointerCaptureRequest pointerCaptureRequest;
-    int32_t pid;
-    nsecs_t consumeTime; // time when the event was consumed by InputConsumer
-    int32_t displayId;
-    float x;
-    float y;
-};
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry,
+                                                       const ui::Transform& rawTransform);
 
 } // namespace android::inputdispatcher
 
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
index 2836467..2c5fe21 100644
--- a/services/inputflinger/dispatcher/EventLogTags.logtags
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -37,6 +37,7 @@
 
 62000 input_interaction (windows|4)
 62001 input_focus (window|3),(reason|3)
+62003 input_cancel (window|3),(reason|3)
 
 # NOTE - the range 1000000-2000000 is reserved for partners and others who
 # want to define their own log tags without conflicting with the core platform.
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index 4a75773..600f02b 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -27,7 +27,7 @@
 
 #include <android-base/stringprintf.h>
 #include <binder/Binder.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
 #include <gui/WindowInfo.h>
 #include <log/log.h>
 
@@ -65,7 +65,7 @@
         if (result == Focusability::OK) {
             return std::nullopt;
         }
-        removeFocusReason = NamedEnum::string(result);
+        removeFocusReason = ftl::enum_string(result);
     }
 
     // We don't have a focused window or the currently focused window is no longer focusable. Check
@@ -79,7 +79,7 @@
         if (result == Focusability::OK) {
             return updateFocusedWindow(displayId,
                                        "Window became focusable. Previous reason: " +
-                                               NamedEnum::string(previousResult),
+                                               ftl::enum_string(previousResult),
                                        requestedFocus, request->windowName);
         }
     }
@@ -116,7 +116,7 @@
                                        request.token, request.windowName);
         }
         ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
-              request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+              request.windowName.c_str(), displayId, ftl::enum_string(result).c_str());
         return std::nullopt;
     }
 
@@ -134,7 +134,7 @@
     // The requested window is not currently focusable. Wait for the window to become focusable
     // but remove focus from the current window so that input events can go into a pending queue
     // and be sent to the window when it becomes focused.
-    return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
+    return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
                                nullptr);
 }
 
@@ -212,7 +212,7 @@
     for (const auto& [displayId, request] : mFocusRequestByDisplay) {
         auto it = mLastFocusResultByDisplay.find(displayId);
         std::string result =
-                it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
+                it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
         dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
                                    displayId, request.windowName.c_str(), result.c_str());
     }
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index 1d6cd9a..6d11a77 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -77,6 +77,8 @@
         NO_WINDOW,
         NOT_FOCUSABLE,
         NOT_VISIBLE,
+
+        ftl_last = NOT_VISIBLE
     };
 
     // Checks if the window token can be focused on a display. The token can be focused if there is
@@ -113,4 +115,4 @@
     std::optional<android::gui::FocusRequest> getFocusRequest(int32_t displayId);
 };
 
-} // namespace android::inputdispatcher
\ No newline at end of file
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d53495f..3f3c0db 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -19,42 +19,12 @@
 
 #define LOG_NDEBUG 1
 
-// Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 0
-
-// Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 0
-
-// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 0
-
-// Log debug messages about channel creation
-#define DEBUG_CHANNEL_CREATION 0
-
-// Log debug messages about input event injection.
-#define DEBUG_INJECTION 0
-
-// Log debug messages about input focus tracking.
-static constexpr bool DEBUG_FOCUS = false;
-
-// Log debug messages about touch occlusion
-// STOPSHIP(b/169067926): Set to false
-static constexpr bool DEBUG_TOUCH_OCCLUSION = true;
-
-// Log debug messages about the app switch latency optimization.
-#define DEBUG_APP_SWITCH 0
-
-// Log debug messages about hover events.
-#define DEBUG_HOVER 0
-
-#include <InputFlingerProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
 #include <binder/Binder.h>
-#include <binder/IServiceManager.h>
-#include <com/android/internal/compat/IPlatformCompatNative.h>
+#include <ftl/enum.h>
 #include <gui/SurfaceComposerClient.h>
 #include <input/InputDevice.h>
 #include <log/log.h>
@@ -82,6 +52,7 @@
 using android::base::HwTimeoutMultiplier;
 using android::base::Result;
 using android::base::StringPrintf;
+using android::gui::DisplayInfo;
 using android::gui::FocusRequest;
 using android::gui::TouchOcclusionMode;
 using android::gui::WindowInfo;
@@ -90,18 +61,51 @@
 using android::os::IInputConstants;
 using android::os::InputEventInjectionResult;
 using android::os::InputEventInjectionSync;
-using com::android::internal::compat::IPlatformCompatNative;
 
 namespace android::inputdispatcher {
 
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-static bool isPerWindowInputRotationEnabled() {
-    static const bool PER_WINDOW_INPUT_ROTATION =
-            sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
+namespace {
 
-    return PER_WINDOW_INPUT_ROTATION;
-}
+// Log detailed debug messages about each inbound event notification to the dispatcher.
+constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false;
+
+// Log detailed debug messages about each outbound event processed by the dispatcher.
+constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false;
+
+// Log debug messages about the dispatch cycle.
+constexpr bool DEBUG_DISPATCH_CYCLE = false;
+
+// Log debug messages about channel creation
+constexpr bool DEBUG_CHANNEL_CREATION = false;
+
+// Log debug messages about input event injection.
+constexpr bool DEBUG_INJECTION = false;
+
+// Log debug messages about input focus tracking.
+constexpr bool DEBUG_FOCUS = false;
+
+// Log debug messages about touch mode event
+constexpr bool DEBUG_TOUCH_MODE = false;
+
+// Log debug messages about touch occlusion
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+
+// Log debug messages about the app switch latency optimization.
+constexpr bool DEBUG_APP_SWITCH = false;
+
+// Log debug messages about hover events.
+constexpr bool DEBUG_HOVER = false;
+
+// Temporarily releases a held mutex for the lifetime of the instance.
+// Named to match std::scoped_lock
+class scoped_unlock {
+public:
+    explicit scoped_unlock(std::mutex& mutex) : mMutex(mutex) { mMutex.unlock(); }
+    ~scoped_unlock() { mMutex.lock(); }
+
+private:
+    std::mutex& mMutex;
+};
 
 // Default input dispatching timeout if there is no focused application or paused window
 // from which to determine an appropriate dispatching timeout.
@@ -135,28 +139,29 @@
 // Event log tags. See EventLogTags.logtags for reference
 constexpr int LOGTAG_INPUT_INTERACTION = 62000;
 constexpr int LOGTAG_INPUT_FOCUS = 62001;
+constexpr int LOGTAG_INPUT_CANCEL = 62003;
 
-static inline nsecs_t now() {
+inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
-static inline const char* toString(bool value) {
+inline const char* toString(bool value) {
     return value ? "true" : "false";
 }
 
-static inline const std::string toString(sp<IBinder> binder) {
+inline const std::string toString(const sp<IBinder>& binder) {
     if (binder == nullptr) {
         return "<null>";
     }
     return StringPrintf("%p", binder.get());
 }
 
-static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+inline int32_t getMotionEventActionPointerIndex(int32_t action) {
     return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
             AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
 }
 
-static bool isValidKeyAction(int32_t action) {
+bool isValidKeyAction(int32_t action) {
     switch (action) {
         case AKEY_EVENT_ACTION_DOWN:
         case AKEY_EVENT_ACTION_UP:
@@ -166,7 +171,7 @@
     }
 }
 
-static bool validateKeyEvent(int32_t action) {
+bool validateKeyEvent(int32_t action) {
     if (!isValidKeyAction(action)) {
         ALOGE("Key event has invalid action code 0x%x", action);
         return false;
@@ -174,7 +179,7 @@
     return true;
 }
 
-static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
+bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
     switch (action & AMOTION_EVENT_ACTION_MASK) {
         case AMOTION_EVENT_ACTION_DOWN:
         case AMOTION_EVENT_ACTION_UP:
@@ -199,12 +204,12 @@
     }
 }
 
-static int64_t millis(std::chrono::nanoseconds t) {
+int64_t millis(std::chrono::nanoseconds t) {
     return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
 }
 
-static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
-                                const PointerProperties* pointerProperties) {
+bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
+                         const PointerProperties* pointerProperties) {
     if (!isValidMotionAction(action, actionButton, pointerCount)) {
         ALOGE("Motion event has invalid action code 0x%x", action);
         return false;
@@ -231,7 +236,7 @@
     return true;
 }
 
-static std::string dumpRegion(const Region& region) {
+std::string dumpRegion(const Region& region) {
     if (region.isEmpty()) {
         return "<empty>";
     }
@@ -252,7 +257,7 @@
     return dump;
 }
 
-static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
     constexpr size_t maxEntries = 50; // max events to print
     constexpr size_t skipBegin = maxEntries / 2;
     const size_t skipEnd = queue.size() - maxEntries / 2;
@@ -291,12 +296,12 @@
  * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned.
  */
 template <typename K, typename V>
-static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
+V getValueByKey(const std::unordered_map<K, V>& map, K key) {
     auto it = map.find(key);
     return it != map.end() ? it->second : V{};
 }
 
-static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
+bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
     if (first == second) {
         return true;
     }
@@ -308,7 +313,7 @@
     return first->getToken() == second->getToken();
 }
 
-static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
+bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
     if (first == nullptr || second == nullptr) {
         return false;
     }
@@ -316,33 +321,18 @@
             first->applicationInfo.token == second->applicationInfo.token;
 }
 
-static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
     return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
 }
 
-static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
-                                                          std::shared_ptr<EventEntry> eventEntry,
-                                                          int32_t inputTargetFlags) {
-    if (eventEntry->type == EventEntry::Type::MOTION) {
-        const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
-        if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
-            (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) {
-            const ui::Transform identityTransform;
-            // Use identity transform for joystick and position-based (touchpad) events because they
-            // don't depend on the window transform.
-            return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
-                                                   1.0f /*globalScaleFactor*/,
-                                                   inputTarget.displayOrientation,
-                                                   inputTarget.displaySize);
-        }
-    }
-
+std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+                                                   std::shared_ptr<EventEntry> eventEntry,
+                                                   int32_t inputTargetFlags) {
     if (inputTarget.useDefaultPointerTransform()) {
         const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
         return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
-                                               inputTarget.globalScaleFactor,
-                                               inputTarget.displayOrientation,
-                                               inputTarget.displaySize);
+                                               inputTarget.displayTransform,
+                                               inputTarget.globalScaleFactor);
     }
 
     ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -384,7 +374,7 @@
                                           motionEntry.yPrecision, motionEntry.xCursorPosition,
                                           motionEntry.yCursorPosition, motionEntry.downTime,
                                           motionEntry.pointerCount, motionEntry.pointerProperties,
-                                          pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */);
+                                          pointerCoords.data());
 
     if (motionEntry.injectionState) {
         combinedMotionEntry->injectionState = motionEntry.injectionState;
@@ -393,27 +383,13 @@
 
     std::unique_ptr<DispatchEntry> dispatchEntry =
             std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
-                                            firstPointerTransform, inputTarget.globalScaleFactor,
-                                            inputTarget.displayOrientation,
-                                            inputTarget.displaySize);
+                                            firstPointerTransform, inputTarget.displayTransform,
+                                            inputTarget.globalScaleFactor);
     return dispatchEntry;
 }
 
-static void addGestureMonitors(const std::vector<Monitor>& monitors,
-                               std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
-                               float yOffset = 0) {
-    if (monitors.empty()) {
-        return;
-    }
-    outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
-    for (const Monitor& monitor : monitors) {
-        outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
-    }
-}
-
-static status_t openInputChannelPair(const std::string& name,
-                                     std::shared_ptr<InputChannel>& serverChannel,
-                                     std::unique_ptr<InputChannel>& clientChannel) {
+status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel,
+                              std::unique_ptr<InputChannel>& clientChannel) {
     std::unique_ptr<InputChannel> uniqueServerChannel;
     status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
 
@@ -422,7 +398,7 @@
 }
 
 template <typename T>
-static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
+bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
     if (lhs == nullptr && rhs == nullptr) {
         return true;
     }
@@ -432,16 +408,7 @@
     return *lhs == *rhs;
 }
 
-static sp<IPlatformCompatNative> getCompatService() {
-    sp<IBinder> service(defaultServiceManager()->getService(String16("platform_compat_native")));
-    if (service == nullptr) {
-        ALOGE("Failed to link to compat service");
-        return nullptr;
-    }
-    return interface_cast<IPlatformCompatNative>(service);
-}
-
-static KeyEvent createKeyEvent(const KeyEntry& entry) {
+KeyEvent createKeyEvent(const KeyEntry& entry) {
     KeyEvent event;
     event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
                      entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
@@ -449,7 +416,7 @@
     return event;
 }
 
-static std::optional<int32_t> findMonitorPidByToken(
+std::optional<int32_t> findMonitorPidByToken(
         const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay,
         const sp<IBinder>& token) {
     for (const auto& it : monitorsByDisplay) {
@@ -463,7 +430,7 @@
     return std::nullopt;
 }
 
-static bool shouldReportMetricsForConnection(const Connection& connection) {
+bool shouldReportMetricsForConnection(const Connection& connection) {
     // Do not keep track of gesture monitors. They receive every event and would disproportionately
     // affect the statistics.
     if (connection.monitor) {
@@ -476,8 +443,7 @@
     return true;
 }
 
-static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry,
-                                      const Connection& connection) {
+bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, const Connection& connection) {
     const EventEntry& eventEntry = *dispatchEntry.eventEntry;
     const int32_t& inputEventId = eventEntry.id;
     if (inputEventId != dispatchEntry.resolvedEventId) {
@@ -513,6 +479,64 @@
     return true;
 }
 
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+bool isConnectionResponsive(const Connection& connection) {
+    const nsecs_t currentTime = now();
+    for (const DispatchEntry* entry : connection.waitQueue) {
+        if (entry->timeoutTime < currentTime) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Returns true if the event type passed as argument represents a user activity.
+bool isUserActivityEvent(const EventEntry& eventEntry) {
+    switch (eventEntry.type) {
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG:
+        case EventEntry::Type::TOUCH_MODE_CHANGED:
+        case EventEntry::Type::SENSOR:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+            return false;
+        case EventEntry::Type::DEVICE_RESET:
+        case EventEntry::Type::KEY:
+        case EventEntry::Type::MOTION:
+            return true;
+    }
+}
+
+// Returns true if the given window can accept pointer events at the given display location.
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
+                          bool isStylus) {
+    if (windowInfo.displayId != displayId || !windowInfo.visible) {
+        return false;
+    }
+    const auto flags = windowInfo.flags;
+    const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus();
+    if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
+        return false;
+    }
+    const bool isModalWindow = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
+            !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    if (!isModalWindow && !windowInfo.touchableRegionContainsPoint(x, y)) {
+        return false;
+    }
+    return true;
+}
+
+bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) {
+    return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) &&
+            (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+             entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER);
+}
+
+} // namespace
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -529,40 +553,38 @@
         // mInTouchMode will be initialized by the WindowManager to the default device config.
         // To avoid leaking stack in case that call never comes, and for tests,
         // initialize it here anyways.
-        mInTouchMode(true),
+        mInTouchMode(kDefaultInTouchMode),
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
         mLatencyAggregator(),
-        mLatencyTracker(&mLatencyAggregator),
-        mCompatService(getCompatService()) {
+        mLatencyTracker(&mLatencyAggregator) {
     mLooper = new Looper(false);
     mReporter = createInputReporter();
 
+    mWindowInfoListener = new DispatcherWindowListener(*this);
+    SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
+
     mKeyRepeatState.lastKeyEntry = nullptr;
 
     policy->getDispatcherConfiguration(&mConfig);
 }
 
 InputDispatcher::~InputDispatcher() {
-    { // acquire lock
-        std::scoped_lock _l(mLock);
+    std::scoped_lock _l(mLock);
 
-        resetKeyRepeatLocked();
-        releasePendingEventLocked();
-        drainInboundQueueLocked();
-    }
+    resetKeyRepeatLocked();
+    releasePendingEventLocked();
+    drainInboundQueueLocked();
+    mCommandQueue.clear();
 
     while (!mConnectionsByToken.empty()) {
         sp<Connection> connection = mConnectionsByToken.begin()->second;
-        removeInputChannel(connection->inputChannel->getConnectionToken());
+        removeInputChannelLocked(connection->inputChannel->getConnectionToken(),
+                                 false /* notify */);
     }
 }
 
-void InputDispatcher::onFirstRef() {
-    SurfaceComposerClient::getDefault()->addWindowInfosListener(this);
-}
-
 status_t InputDispatcher::start() {
     if (mThread) {
         return ALREADY_EXISTS;
@@ -595,7 +617,7 @@
 
         // Run all pending commands if there are any.
         // If any commands were run then force the next poll to wake up immediately.
-        if (runCommandsLockedInterruptible()) {
+        if (runCommandsLockedInterruptable()) {
             nextWakeupTime = LONG_LONG_MIN;
         }
 
@@ -798,6 +820,14 @@
             break;
         }
 
+        case EventEntry::Type::TOUCH_MODE_CHANGED: {
+            const auto typedEntry = std::static_pointer_cast<TouchModeEntry>(mPendingEvent);
+            dispatchTouchModeChangeLocked(currentTime, typedEntry);
+            done = true;
+            dropReason = DropReason::NOT_DROPPED; // touch mode events are never dropped
+            break;
+        }
+
         case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
             const auto typedEntry =
                     std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
@@ -885,7 +915,7 @@
  */
 bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
     const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
-            (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
+            isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER);
 
     // Optimize case where the current application is unresponsive and the user
     // decides to touch a window in a different application.
@@ -897,8 +927,10 @@
                 motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
         int32_t y = static_cast<int32_t>(
                 motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+
+        const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
         sp<WindowInfoHandle> touchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, nullptr);
+                findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus);
         if (touchedWindowHandle != nullptr &&
             touchedWindowHandle->getApplicationToken() !=
                     mAwaitedFocusedApplication->getApplicationToken()) {
@@ -910,11 +942,9 @@
         }
 
         // Alternatively, maybe there's a gesture monitor that could handle this event
-        std::vector<TouchedMonitor> gestureMonitors =
-                findTouchedGestureMonitorsLocked(displayId, {});
-        for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+        for (const auto& monitor : getValueByKey(mGestureMonitorsByDisplay, displayId)) {
             sp<Connection> connection =
-                    getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+                    getConnectionLocked(monitor.inputChannel->getConnectionToken());
             if (connection != nullptr && connection->responsive) {
                 // This monitor could take more input. Drop all events preceding this
                 // event, so that gesture monitor could get a chance to receive the stream
@@ -956,9 +986,9 @@
                     mAppSwitchSawKeyDown = true;
                 } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
                     if (mAppSwitchSawKeyDown) {
-#if DEBUG_APP_SWITCH
-                        ALOGD("App switch is pending!");
-#endif
+                        if (DEBUG_APP_SWITCH) {
+                            ALOGD("App switch is pending!");
+                        }
                         mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
                         mAppSwitchSawKeyDown = false;
                         needWake = true;
@@ -979,6 +1009,7 @@
             LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
             break;
         }
+        case EventEntry::Type::TOUCH_MODE_CHANGED:
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
@@ -1004,76 +1035,59 @@
 
 sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                 int32_t y, TouchState* touchState,
+                                                                bool isStylus,
                                                                 bool addOutsideTargets,
-                                                                bool addPortalWindows,
                                                                 bool ignoreDragWindow) {
-    if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
-        LOG_ALWAYS_FATAL(
-                "Must provide a valid touch state if adding portal windows or outside targets");
+    if (addOutsideTargets && touchState == nullptr) {
+        LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
     }
     // Traverse windows from front to back to find touched window.
-    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    const auto& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
         if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
             continue;
         }
-        const WindowInfo* windowInfo = windowHandle->getInfo();
-        if (windowInfo->displayId == displayId) {
-            auto flags = windowInfo->flags;
 
-            if (windowInfo->visible) {
-                if (!flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
-                    bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
-                            !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
-                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
-                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
-                        if (portalToDisplayId != ADISPLAY_ID_NONE &&
-                            portalToDisplayId != displayId) {
-                            if (addPortalWindows) {
-                                // For the monitoring channels of the display.
-                                touchState->addPortalWindow(windowHandle);
-                            }
-                            return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
-                                                             addOutsideTargets, addPortalWindows);
-                        }
-                        // Found window.
-                        return windowHandle;
-                    }
-                }
+        const WindowInfo& info = *windowHandle->getInfo();
+        if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+            return windowHandle;
+        }
 
-                if (addOutsideTargets && flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
-                    touchState->addOrUpdateWindow(windowHandle,
-                                                  InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
-                                                  BitSet32(0));
-                }
-            }
+        if (addOutsideTargets && info.flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
+            touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+                                          BitSet32(0));
         }
     }
     return nullptr;
 }
 
-std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
-        int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) const {
-    std::vector<TouchedMonitor> touchedMonitors;
+std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
+        int32_t displayId, int32_t x, int32_t y, bool isStylus) const {
+    // Traverse windows from front to back and gather the touched spy windows.
+    std::vector<sp<WindowInfoHandle>> spyWindows;
+    const auto& windowHandles = getWindowHandlesLocked(displayId);
+    for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
+        const WindowInfo& info = *windowHandle->getInfo();
 
-    std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
-    addGestureMonitors(monitors, touchedMonitors);
-    for (const sp<WindowInfoHandle>& portalWindow : portalWindows) {
-        const WindowInfo* windowInfo = portalWindow->getInfo();
-        monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
-        addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft,
-                           -windowInfo->frameTop);
+        if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+            continue;
+        }
+        if (!info.isSpy()) {
+            // The first touched non-spy window was found, so return the spy windows touched so far.
+            return spyWindows;
+        }
+        spyWindows.push_back(windowHandle);
     }
-    return touchedMonitors;
+    return spyWindows;
 }
 
 void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
     const char* reason;
     switch (dropReason) {
         case DropReason::POLICY:
-#if DEBUG_INBOUND_EVENT_DETAILS
-            ALOGD("Dropped event because policy consumed it.");
-#endif
+            if (DEBUG_INBOUND_EVENT_DETAILS) {
+                ALOGD("Dropped event because policy consumed it.");
+            }
             reason = "inbound event was dropped because the policy consumed it";
             break;
         case DropReason::DISABLED:
@@ -1131,9 +1145,10 @@
             break;
         }
         case EventEntry::Type::FOCUS:
+        case EventEntry::Type::TOUCH_MODE_CHANGED:
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET: {
-            LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str());
+            LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str());
             break;
         }
     }
@@ -1157,37 +1172,35 @@
 void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
     mAppSwitchDueTime = LONG_LONG_MAX;
 
-#if DEBUG_APP_SWITCH
-    if (handled) {
-        ALOGD("App switch has arrived.");
-    } else {
-        ALOGD("App switch was abandoned.");
+    if (DEBUG_APP_SWITCH) {
+        if (handled) {
+            ALOGD("App switch has arrived.");
+        } else {
+            ALOGD("App switch was abandoned.");
+        }
     }
-#endif
 }
 
 bool InputDispatcher::haveCommandsLocked() const {
     return !mCommandQueue.empty();
 }
 
-bool InputDispatcher::runCommandsLockedInterruptible() {
+bool InputDispatcher::runCommandsLockedInterruptable() {
     if (mCommandQueue.empty()) {
         return false;
     }
 
     do {
-        std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
+        auto command = std::move(mCommandQueue.front());
         mCommandQueue.pop_front();
-        Command command = commandEntry->command;
-        command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
-
-        commandEntry->connection.clear();
+        // Commands are run with the lock held, but may release and re-acquire the lock from within.
+        command();
     } while (!mCommandQueue.empty());
     return true;
 }
 
-void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
-    mCommandQueue.push_back(std::move(commandEntry));
+void InputDispatcher::postCommandLocked(Command&& command) {
+    mCommandQueue.push_back(command);
 }
 
 void InputDispatcher::drainInboundQueueLocked() {
@@ -1209,9 +1222,9 @@
 void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
     InjectionState* injectionState = entry->injectionState;
     if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
-#if DEBUG_DISPATCH_CYCLE
-        ALOGD("Injected inbound event was dropped.");
-#endif
+        if (DEBUG_DISPATCH_CYCLE) {
+            ALOGD("Injected inbound event was dropped.");
+        }
         setInjectionResult(*entry, InputEventInjectionResult::FAILED);
     }
     if (entry == mNextUnblockedEvent) {
@@ -1246,27 +1259,28 @@
 
 bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
                                                          const ConfigurationChangedEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
+    }
 
     // Reset key repeating in case a keyboard device was added or removed or something.
     resetKeyRepeatLocked();
 
     // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
-    commandEntry->eventTime = entry.eventTime;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyConfigurationChanged(eventTime);
+    };
+    postCommandLocked(std::move(command));
     return true;
 }
 
 bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
                                                 const DeviceResetEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
-          entry.deviceId);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
+              entry.deviceId);
+    }
 
     // Reset key repeating in case a keyboard device was disabled or enabled.
     if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId == entry.deviceId) {
@@ -1390,6 +1404,43 @@
     dropReason = DropReason::NOT_DROPPED;
 }
 
+void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime,
+                                                    const std::shared_ptr<TouchModeEntry>& entry) {
+    const std::vector<sp<WindowInfoHandle>>& windowHandles =
+            getWindowHandlesLocked(mFocusedDisplayId);
+    if (windowHandles.empty()) {
+        return;
+    }
+    const std::vector<InputTarget> inputTargets =
+            getInputTargetsFromWindowHandlesLocked(windowHandles);
+    if (inputTargets.empty()) {
+        return;
+    }
+    entry->dispatchInProgress = true;
+    dispatchEventLocked(currentTime, entry, inputTargets);
+}
+
+std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked(
+        const std::vector<sp<WindowInfoHandle>>& windowHandles) const {
+    std::vector<InputTarget> inputTargets;
+    for (const sp<WindowInfoHandle>& handle : windowHandles) {
+        // TODO(b/193718270): Due to performance concerns, consider notifying visible windows only.
+        const sp<IBinder>& token = handle->getToken();
+        if (token == nullptr) {
+            continue;
+        }
+        std::shared_ptr<InputChannel> channel = getInputChannelLocked(token);
+        if (channel == nullptr) {
+            continue; // Window has gone away
+        }
+        InputTarget target;
+        target.inputChannel = channel;
+        target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+        inputTargets.push_back(target);
+    }
+    return inputTargets;
+}
+
 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                         DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Preprocessing.
@@ -1421,9 +1472,9 @@
         } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
                    mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
             // The key on device 'deviceId' is still down, do not stop key repeat
-#if DEBUG_INBOUND_EVENT_DETAILS
-            ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
-#endif
+            if (DEBUG_INBOUND_EVENT_DETAILS) {
+                ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+            }
         } else if (!entry->syntheticRepeat) {
             resetKeyRepeatLocked();
         }
@@ -1454,13 +1505,13 @@
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
-            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
             sp<IBinder> focusedWindowToken =
                     mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
-            commandEntry->connectionToken = focusedWindowToken;
-            commandEntry->keyEntry = entry;
-            postCommandLocked(std::move(commandEntry));
+
+            auto command = [this, focusedWindowToken, entry]() REQUIRES(mLock) {
+                doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
+            };
+            postCommandLocked(std::move(command));
             return false; // wait for the command to run
         } else {
             entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
@@ -1502,47 +1553,42 @@
 }
 
 void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
-          "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
-          "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
-          prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
-          entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
-          entry.repeatCount, entry.downTime);
-#endif
-}
-
-void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    const std::shared_ptr<SensorEntry>& entry = commandEntry->sensorEntry;
-    if (entry->accuracyChanged) {
-        mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
+              "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
+              "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
+              prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
+              entry.policyFlags, entry.action, entry.flags, entry.keyCode, entry.scanCode,
+              entry.metaState, entry.repeatCount, entry.downTime);
     }
-    mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
-                               entry->hwTimestamp, entry->values);
-    mLock.lock();
 }
 
-void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
+                                           const std::shared_ptr<SensorEntry>& entry,
                                            DropReason* dropReason, nsecs_t* nextWakeupTime) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
-          "source=0x%x, sensorType=%s",
-          entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
-          NamedEnum::string(entry->sensorType).c_str());
-#endif
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
-    commandEntry->sensorEntry = entry;
-    postCommandLocked(std::move(commandEntry));
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
+              "source=0x%x, sensorType=%s",
+              entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
+              ftl::enum_string(entry->sensorType).c_str());
+    }
+    auto command = [this, entry]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+
+        if (entry->accuracyChanged) {
+            mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+        }
+        mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+                                   entry->hwTimestamp, entry->values);
+    };
+    postCommandLocked(std::move(command));
 }
 
 bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
-          NamedEnum::string(sensorType).c_str());
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
+              ftl::enum_string(sensorType).c_str());
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -1575,7 +1621,7 @@
         return true;
     }
 
-    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
+    const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);
 
     // Identify targets.
     std::vector<InputTarget> inputTargets;
@@ -1613,23 +1659,6 @@
     // Add monitor channels from event's or focused display.
     addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
-    if (isPointerEvent) {
-        std::unordered_map<int32_t, TouchState>::iterator it =
-                mTouchStatesByDisplay.find(entry->displayId);
-        if (it != mTouchStatesByDisplay.end()) {
-            const TouchState& state = it->second;
-            if (!state.portalWindows.empty()) {
-                // The event has gone through these portal windows, so we add monitoring targets of
-                // the corresponding displays as well.
-                for (size_t i = 0; i < state.portalWindows.size(); i++) {
-                    const WindowInfo* windowInfo = state.portalWindows[i]->getInfo();
-                    addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
-                                                     -windowInfo->frameLeft, -windowInfo->frameTop);
-                }
-            }
-        }
-    }
-
     // Dispatch the motion.
     if (conflictingPointerActions) {
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -1670,42 +1699,43 @@
 }
 
 void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
-          ", policyFlags=0x%x, "
-          "action=0x%x, actionButton=0x%x, flags=0x%x, "
-          "metaState=0x%x, buttonState=0x%x,"
-          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-          prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
-          entry.action, entry.actionButton, entry.flags, entry.metaState, entry.buttonState,
-          entry.edgeFlags, entry.xPrecision, entry.yPrecision, entry.downTime);
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+              ", policyFlags=0x%x, "
+              "action=%s, actionButton=0x%x, flags=0x%x, "
+              "metaState=0x%x, buttonState=0x%x,"
+              "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
+              prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
+              entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
+              entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
+              entry.xPrecision, entry.yPrecision, entry.downTime);
 
-    for (uint32_t i = 0; i < entry.pointerCount; i++) {
-        ALOGD("  Pointer %d: id=%d, toolType=%d, "
-              "x=%f, y=%f, pressure=%f, size=%f, "
-              "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
-              "orientation=%f",
-              i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+        for (uint32_t i = 0; i < entry.pointerCount; i++) {
+            ALOGD("  Pointer %d: id=%d, toolType=%d, "
+                  "x=%f, y=%f, pressure=%f, size=%f, "
+                  "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+                  "orientation=%f",
+                  i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+        }
     }
-#endif
 }
 
 void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
                                           std::shared_ptr<EventEntry> eventEntry,
                                           const std::vector<InputTarget>& inputTargets) {
     ATRACE_CALL();
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("dispatchEventToCurrentInputTargets");
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("dispatchEventToCurrentInputTargets");
+    }
 
     updateInteractionTokensLocked(*eventEntry, inputTargets);
 
@@ -1736,7 +1766,7 @@
     // pile up.
     ALOGW("Canceling events for %s because it is unresponsive",
           connection->inputChannel->getName().c_str());
-    if (connection->status == Connection::STATUS_NORMAL) {
+    if (connection->status == Connection::Status::NORMAL) {
         CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
                                    "application not responding");
         synthesizeCancelationEventsForConnectionLocked(connection, options);
@@ -1771,13 +1801,14 @@
             displayId = motionEntry.displayId;
             break;
         }
+        case EventEntry::Type::TOUCH_MODE_CHANGED:
         case EventEntry::Type::POINTER_CAPTURE_CHANGED:
         case EventEntry::Type::FOCUS:
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
         case EventEntry::Type::DRAG: {
-            ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
+            ALOGE("%s events do not have a target display", ftl::enum_string(entry.type).c_str());
             return ADISPLAY_ID_NONE;
         }
     }
@@ -1829,7 +1860,7 @@
     if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
         ALOGI("Dropping %s event because there is no focused window or focused application in "
               "display %" PRId32 ".",
-              NamedEnum::string(entry.type).c_str(), displayId);
+              ftl::enum_string(entry.type).c_str(), displayId);
         return InputEventInjectionResult::FAILED;
     }
 
@@ -1859,7 +1890,7 @@
         } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
             // Already raised ANR. Drop the event
             ALOGE("Dropping %s event because there is no focused window",
-                  NamedEnum::string(entry.type).c_str());
+                  ftl::enum_string(entry.type).c_str());
             return InputEventInjectionResult::FAILED;
         } else {
             // Still waiting for the focused window
@@ -1911,16 +1942,16 @@
  * Given a list of monitors, remove the ones we cannot find a connection for, and the ones
  * that are currently unresponsive.
  */
-std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
-        const std::vector<TouchedMonitor>& monitors) const {
-    std::vector<TouchedMonitor> responsiveMonitors;
+std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked(
+        const std::vector<Monitor>& monitors) const {
+    std::vector<Monitor> responsiveMonitors;
     std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
-                 [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
-                     sp<Connection> connection = getConnectionLocked(
-                             monitor.monitor.inputChannel->getConnectionToken());
+                 [this](const Monitor& monitor) REQUIRES(mLock) {
+                     sp<Connection> connection =
+                             getConnectionLocked(monitor.inputChannel->getConnectionToken());
                      if (connection == nullptr) {
                          ALOGE("Could not find connection for monitor %s",
-                               monitor.monitor.inputChannel->getName().c_str());
+                               monitor.inputChannel->getName().c_str());
                          return false;
                      }
                      if (!connection->responsive) {
@@ -1945,9 +1976,9 @@
 
     // For security reasons, we defer updating the touch state until we are sure that
     // event injection will be allowed.
-    int32_t displayId = entry.displayId;
-    int32_t action = entry.action;
-    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+    const int32_t displayId = entry.displayId;
+    const int32_t action = entry.action;
+    const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
     InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
@@ -1960,10 +1991,8 @@
     // If no state for the specified display exists, then our initial state will be empty.
     const TouchState* oldState = nullptr;
     TouchState tempTouchState;
-    std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
-            mTouchStatesByDisplay.find(displayId);
-    if (oldStateIt != mTouchStatesByDisplay.end()) {
-        oldState = &(oldStateIt->second);
+    if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
+        oldState = &(it->second);
         tempTouchState.copyFrom(*oldState);
     }
 
@@ -1971,12 +2000,13 @@
     bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
             (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
              tempTouchState.displayId != displayId);
-    bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
-                          maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
-                          maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
-    bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
-                       maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
-    const bool isFromMouse = entry.source == AINPUT_SOURCE_MOUSE;
+
+    const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
+                                maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+                                maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
+    const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+                             maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
+    const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
     bool wrongDevice = false;
     if (newGesture) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
@@ -2012,7 +2042,7 @@
 
         int32_t x;
         int32_t y;
-        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+        const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
         // Always dispatch mouse events to cursor position.
         if (isFromMouse) {
             x = int32_t(entry.xCursorPosition);
@@ -2021,101 +2051,110 @@
             x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
             y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
         }
-        bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        newTouchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
-                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
-
-        // Figure out whether splitting will be allowed for this window.
-        if (newTouchedWindowHandle != nullptr &&
-            newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
-            // New window supports splitting, but we should never split mouse events.
-            isSplit = !isFromMouse;
-        } else if (isSplit) {
-            // New window does not support splitting but we have already split events.
-            // Ignore the new window.
-            newTouchedWindowHandle = nullptr;
-        }
+        const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
+        const bool isStylus = isPointerFromStylus(entry, pointerIndex);
+        newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+                                                           isStylus, isDown /*addOutsideTargets*/);
 
         // Handle the case where we did not find a window.
         if (newTouchedWindowHandle == nullptr) {
+            ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
+                  displayId);
             // Try to assign the pointer to the first foreground window we find, if there is one.
             newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
         }
 
-        if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {
-            ALOGI("Not sending touch event to %s because it is paused",
-                  newTouchedWindowHandle->getName().c_str());
-            newTouchedWindowHandle = nullptr;
-        }
-
-        // Ensure the window has a connection and the connection is responsive
+        // Figure out whether splitting will be allowed for this window.
         if (newTouchedWindowHandle != nullptr) {
-            const bool isResponsive = hasResponsiveConnectionLocked(*newTouchedWindowHandle);
-            if (!isResponsive) {
-                ALOGW("%s will not receive the new gesture at %" PRIu64,
-                      newTouchedWindowHandle->getName().c_str(), entry.eventTime);
+            if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
+                // New window supports splitting, but we should never split mouse events.
+                isSplit = !isFromMouse;
+            } else if (isSplit) {
+                // New window does not support splitting but we have already split events.
+                // Ignore the new window.
                 newTouchedWindowHandle = nullptr;
             }
+        } else {
+            // No window is touched, so set split to true. This will allow the next pointer down to
+            // be delivered to a new window which supports split touch. Pointers from a mouse device
+            // should never be split.
+            tempTouchState.split = isSplit = !isFromMouse;
         }
 
-        // Drop events that can't be trusted due to occlusion
-        if (newTouchedWindowHandle != nullptr &&
-            mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
-            TouchOcclusionInfo occlusionInfo =
-                    computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
-            if (!isTouchTrustedLocked(occlusionInfo)) {
-                if (DEBUG_TOUCH_OCCLUSION) {
-                    ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
-                    for (const auto& log : occlusionInfo.debugInfo) {
-                        ALOGD("%s", log.c_str());
-                    }
-                }
-                onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
-                if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
-                    ALOGW("Dropping untrusted touch event due to %s/%d",
-                          occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
-                    newTouchedWindowHandle = nullptr;
-                }
-            }
-        }
-
-        // Drop touch events if requested by input feature
-        if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) {
-            newTouchedWindowHandle = nullptr;
-        }
-
-        const std::vector<TouchedMonitor> newGestureMonitors = isDown
-                ? selectResponsiveMonitorsLocked(
-                          findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows))
-                : tempTouchState.gestureMonitors;
-
-        if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
-            ALOGI("Dropping event because there is no touchable window or gesture monitor at "
-                  "(%d, %d) in display %" PRId32 ".",
-                  x, y, displayId);
-            injectionResult = InputEventInjectionResult::FAILED;
-            goto Failed;
-        }
-
+        // Update hover state.
         if (newTouchedWindowHandle != nullptr) {
-            // Set target flags.
-            int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
-            if (isSplit) {
-                targetFlags |= InputTarget::FLAG_SPLIT;
-            }
-            if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
-                targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-            } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
-                targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
-            }
-
-            // Update hover state.
             if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
                 newHoverWindowHandle = nullptr;
             } else if (isHoverAction) {
                 newHoverWindowHandle = newTouchedWindowHandle;
             }
+        }
+
+        std::vector<sp<WindowInfoHandle>> newTouchedWindows =
+                findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
+        if (newTouchedWindowHandle != nullptr) {
+            // Process the foreground window first so that it is the first to receive the event.
+            newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
+        }
+
+        for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
+            const WindowInfo& info = *windowHandle->getInfo();
+
+            if (info.paused) {
+                ALOGI("Not sending touch event to %s because it is paused",
+                      windowHandle->getName().c_str());
+                continue;
+            }
+
+            // Ensure the window has a connection and the connection is responsive
+            const bool isResponsive = hasResponsiveConnectionLocked(*windowHandle);
+            if (!isResponsive) {
+                ALOGW("Not sending touch gesture to %s because it is not responsive",
+                      windowHandle->getName().c_str());
+                continue;
+            }
+
+            // Drop events that can't be trusted due to occlusion
+            if (mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
+                TouchOcclusionInfo occlusionInfo =
+                        computeTouchOcclusionInfoLocked(windowHandle, x, y);
+                if (!isTouchTrustedLocked(occlusionInfo)) {
+                    if (DEBUG_TOUCH_OCCLUSION) {
+                        ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+                        for (const auto& log : occlusionInfo.debugInfo) {
+                            ALOGD("%s", log.c_str());
+                        }
+                    }
+                    sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
+                    if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
+                        ALOGW("Dropping untrusted touch event due to %s/%d",
+                              occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
+                        continue;
+                    }
+                }
+            }
+
+            // Drop touch events if requested by input feature
+            if (shouldDropInput(entry, windowHandle)) {
+                continue;
+            }
+
+            // Set target flags.
+            int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+            if (!info.isSpy()) {
+                // There should only be one new foreground (non-spy) window at this location.
+                targetFlags |= InputTarget::FLAG_FOREGROUND;
+            }
+
+            if (isSplit) {
+                targetFlags |= InputTarget::FLAG_SPLIT;
+            }
+            if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
+                targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+            } else if (isWindowObscuredLocked(windowHandle)) {
+                targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+            }
 
             // Update the temporary touch state.
             BitSet32 pointerIds;
@@ -2123,15 +2162,26 @@
                 uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
                 pointerIds.markBit(pointerId);
             }
-            tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
-        } else if (tempTouchState.windows.empty()) {
-            // If no window is touched, set split to true. This will allow the next pointer down to
-            // be delivered to a new window which supports split touch.
-            tempTouchState.split = true;
+
+            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
         }
-        if (isDown) {
-            tempTouchState.addGestureMonitors(newGestureMonitors);
+
+        const std::vector<Monitor> newGestureMonitors = isDown
+                ? selectResponsiveMonitorsLocked(
+                          getValueByKey(mGestureMonitorsByDisplay, displayId))
+                : std::vector<Monitor>{};
+
+        if (newTouchedWindows.empty() && newGestureMonitors.empty() &&
+            tempTouchState.gestureMonitors.empty()) {
+            ALOGI("Dropping event because there is no touchable window or gesture monitor at "
+                  "(%d, %d) in display %" PRId32 ".",
+                  x, y, displayId);
+            injectionResult = InputEventInjectionResult::FAILED;
+            goto Failed;
         }
+
+        tempTouchState.addGestureMonitors(newGestureMonitors);
+
     } else {
         /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
 
@@ -2151,12 +2201,14 @@
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
             tempTouchState.isSlippery()) {
-            int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-            int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+            const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+            const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
+            const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
             sp<WindowInfoHandle> oldTouchedWindowHandle =
                     tempTouchState.getFirstForegroundWindowHandle();
-            newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+            newTouchedWindowHandle =
+                    findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);
 
             // Drop touch events if requested by input feature
             if (newTouchedWindowHandle != nullptr &&
@@ -2178,7 +2230,7 @@
 
                 // Make a slippery entrance into the new window.
                 if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
-                    isSplit = true;
+                    isSplit = !isFromMouse;
                 }
 
                 int32_t targetFlags =
@@ -2201,16 +2253,17 @@
         }
     }
 
+    // Update dispatching for hover enter and exit.
     if (newHoverWindowHandle != mLastHoverWindowHandle) {
-        // Let the previous window know that the hover sequence is over, unless we already did it
-        // when dispatching it as is to newTouchedWindowHandle.
+        // Let the previous window know that the hover sequence is over, unless we already did
+        // it when dispatching it as is to newTouchedWindowHandle.
         if (mLastHoverWindowHandle != nullptr &&
             (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
              mLastHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
-            ALOGD("Sending hover exit event to window %s.",
-                  mLastHoverWindowHandle->getName().c_str());
-#endif
+            if (DEBUG_HOVER) {
+                ALOGD("Sending hover exit event to window %s.",
+                      mLastHoverWindowHandle->getName().c_str());
+            }
             tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
                                              InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
         }
@@ -2220,10 +2273,10 @@
         if (newHoverWindowHandle != nullptr &&
             (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
              newHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
-            ALOGD("Sending hover enter event to window %s.",
-                  newHoverWindowHandle->getName().c_str());
-#endif
+            if (DEBUG_HOVER) {
+                ALOGD("Sending hover enter event to window %s.",
+                      newHoverWindowHandle->getName().c_str());
+            }
             tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
                                              InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
                                              BitSet32(0));
@@ -2233,10 +2286,17 @@
     // Check permission to inject into all touched foreground windows and ensure there
     // is at least one touched foreground window.
     {
-        bool haveForegroundWindow = false;
+        bool haveForegroundOrSpyWindow = false;
         for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
-            if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
-                haveForegroundWindow = true;
+            const bool isForeground =
+                    (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0;
+            if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+                haveForegroundOrSpyWindow = true;
+                LOG_ALWAYS_FATAL_IF(isForeground,
+                                    "Spy window cannot be dispatched as a foreground window.");
+            }
+            if (isForeground) {
+                haveForegroundOrSpyWindow = true;
                 if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
                     injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
                     injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -2245,8 +2305,8 @@
             }
         }
         bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
-        if (!haveForegroundWindow && !hasGestureMonitor) {
-            ALOGI("Dropping event because there is no touched foreground window in display "
+        if (!haveForegroundOrSpyWindow && !hasGestureMonitor) {
+            ALOGI("Dropping event because there is no touched window in display "
                   "%" PRId32 " or gesture monitor to receive it.",
                   displayId);
             injectionResult = InputEventInjectionResult::FAILED;
@@ -2313,9 +2373,8 @@
                               touchedWindow.pointerIds, inputTargets);
     }
 
-    for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
-        addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
-                                  touchedMonitor.yOffset, inputTargets);
+    for (const auto& monitor : tempTouchState.gestureMonitors) {
+        addMonitoringTargetLocked(monitor, displayId, inputTargets);
     }
 
     // Drop the outside or hover touch windows since we will not care about them
@@ -2411,15 +2470,18 @@
 }
 
 void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
+    // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until we
+    // have an explicit reason to support it.
+    constexpr bool isStylus = false;
+
     const sp<WindowInfoHandle> dropWindow =
-            findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
-                                      false /*addOutsideTargets*/, false /*addPortalWindows*/,
-                                      true /*ignoreDragWindow*/);
+            findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, isStylus,
+                                      false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
     if (dropWindow) {
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
-        notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+        sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
     } else {
-        notifyDropWindowLocked(nullptr, 0, 0);
+        sendDropWindowCommandLocked(nullptr, 0, 0);
     }
     mDragState.reset();
 }
@@ -2446,10 +2508,13 @@
             return;
         }
 
+        // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until
+        // we have an explicit reason to support it.
+        constexpr bool isStylus = false;
+
         const sp<WindowInfoHandle> hoverWindowHandle =
-                findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
-                                          false /*addOutsideTargets*/, false /*addPortalWindows*/,
-                                          true /*ignoreDragWindow*/);
+                findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, isStylus,
+                                          false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
         // enqueue drag exit if needed.
         if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
             !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -2466,7 +2531,7 @@
     } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
         finishDragAndDrop(entry.displayId, x, y);
     } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-        notifyDropWindowLocked(nullptr, 0, 0);
+        sendDropWindowCommandLocked(nullptr, 0, 0);
         mDragState.reset();
     }
 }
@@ -2494,9 +2559,12 @@
         inputTarget.inputChannel = inputChannel;
         inputTarget.flags = targetFlags;
         inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
-        inputTarget.displayOrientation = windowInfo->displayOrientation;
-        inputTarget.displaySize =
-                int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
+        const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId);
+        if (displayInfoIt != mDisplayInfos.end()) {
+            inputTarget.displayTransform = displayInfoIt->second.transform;
+        } else {
+            ALOGE("DisplayInfo not found for window on display: %d", windowInfo->displayId);
+        }
         inputTargets.push_back(inputTarget);
         it = inputTargets.end() - 1;
     }
@@ -2508,27 +2576,29 @@
 }
 
 void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
-                                                       int32_t displayId, float xOffset,
-                                                       float yOffset) {
+                                                       int32_t displayId) {
     std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
             mGlobalMonitorsByDisplay.find(displayId);
 
     if (it != mGlobalMonitorsByDisplay.end()) {
         const std::vector<Monitor>& monitors = it->second;
         for (const Monitor& monitor : monitors) {
-            addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets);
+            addMonitoringTargetLocked(monitor, displayId, inputTargets);
         }
     }
 }
 
-void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset,
-                                                float yOffset,
+void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId,
                                                 std::vector<InputTarget>& inputTargets) {
     InputTarget target;
     target.inputChannel = monitor.inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
     ui::Transform t;
-    t.set(xOffset, yOffset);
+    if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+        const auto& displayTransform = it->second.transform;
+        target.displayTransform = displayTransform;
+        t = displayTransform;
+    }
     target.setDefaultPointerTransform(t);
     inputTargets.push_back(target);
 }
@@ -2665,8 +2735,7 @@
                         "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
                         "], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, "
                         "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
-                        (isTouchedWindow) ? "[TOUCHED] " : "",
-                        NamedEnum::string(info->type, "%" PRId32).c_str(),
+                        isTouchedWindow ? "[TOUCHED] " : "", ftl::enum_string(info->type).c_str(),
                         info->packageName.c_str(), info->ownerUid, info->id,
                         toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
                         info->frameTop, info->frameRight, info->frameBottom,
@@ -2742,11 +2811,8 @@
 }
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
-    if (eventEntry.type == EventEntry::Type::FOCUS ||
-        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
-        eventEntry.type == EventEntry::Type::DRAG) {
-        // Focus or pointer capture changed events are passed to apps, but do not represent user
-        // activity.
+    if (!isUserActivityEvent(eventEntry)) {
+        // Not poking user activity if the event type does not represent a user activity
         return;
     }
     int32_t displayId = getTargetDisplayId(eventEntry);
@@ -2754,9 +2820,9 @@
     if (focusedWindowHandle != nullptr) {
         const WindowInfo* info = focusedWindowHandle->getInfo();
         if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
-#if DEBUG_DISPATCH_CYCLE
-            ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
-#endif
+            if (DEBUG_DISPATCH_CYCLE) {
+                ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
+            }
             return;
         }
     }
@@ -2782,24 +2848,19 @@
             eventType = USER_ACTIVITY_EVENT_BUTTON;
             break;
         }
-        case EventEntry::Type::FOCUS:
-        case EventEntry::Type::CONFIGURATION_CHANGED:
-        case EventEntry::Type::DEVICE_RESET:
-        case EventEntry::Type::SENSOR:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
-        case EventEntry::Type::DRAG: {
+        default: {
             LOG_ALWAYS_FATAL("%s events are not user activity",
-                             NamedEnum::string(eventEntry.type).c_str());
+                             ftl::enum_string(eventEntry.type).c_str());
             break;
         }
     }
 
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
-    commandEntry->eventTime = eventEntry.eventTime;
-    commandEntry->userActivityEventType = eventType;
-    commandEntry->displayId = displayId;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
+                           REQUIRES(mLock) {
+                               scoped_unlock unlock(mLock);
+                               mPolicy->pokeUserActivity(eventTime, eventType, displayId);
+                           };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
@@ -2812,21 +2873,22 @@
                              connection->getInputChannelName().c_str(), eventEntry->id);
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-          "globalScaleFactor=%f, pointerIds=0x%x %s",
-          connection->getInputChannelName().c_str(), inputTarget.flags,
-          inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
-          inputTarget.getPointerInfoString().c_str());
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
+              "globalScaleFactor=%f, pointerIds=0x%x %s",
+              connection->getInputChannelName().c_str(), inputTarget.flags,
+              inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+              inputTarget.getPointerInfoString().c_str());
+    }
 
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
-    if (connection->status != Connection::STATUS_NORMAL) {
-#if DEBUG_DISPATCH_CYCLE
-        ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
-              connection->getInputChannelName().c_str(), connection->getStatusLabel());
-#endif
+    if (connection->status != Connection::Status::NORMAL) {
+        if (DEBUG_DISPATCH_CYCLE) {
+            ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
+                  connection->getInputChannelName().c_str(),
+                  ftl::enum_string(connection->status).c_str());
+        }
         return;
     }
 
@@ -2834,7 +2896,7 @@
     if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
         LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
                             "Entry type %s should not have FLAG_SPLIT",
-                            NamedEnum::string(eventEntry->type).c_str());
+                            ftl::enum_string(eventEntry->type).c_str());
 
         const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
         if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
@@ -2843,6 +2905,11 @@
             if (!splitMotionEntry) {
                 return; // split event was dropped
             }
+            if (splitMotionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+                std::string reason = std::string("reason=pointer cancel on split window");
+                android_log_event_list(LOGTAG_INPUT_CANCEL)
+                        << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
+            }
             if (DEBUG_FOCUS) {
                 ALOGD("channel '%s' ~ Split motion event.",
                       connection->getInputChannelName().c_str());
@@ -2925,10 +2992,11 @@
 
             if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
                                                  dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
-                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
-                      connection->getInputChannelName().c_str());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key "
+                          "event",
+                          connection->getInputChannelName().c_str());
+                }
                 return; // skip the inconsistent event
             }
             break;
@@ -2958,11 +3026,11 @@
             if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
                 !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
                                                    motionEntry.displayId)) {
-#if DEBUG_DISPATCH_CYCLE
-                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
-                      "event",
-                      connection->getInputChannelName().c_str());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover "
+                          "enter event",
+                          connection->getInputChannelName().c_str());
+                }
                 // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because
                 // this is a one-to-one event conversion.
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
@@ -2978,11 +3046,11 @@
 
             if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
                                                     dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
-                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
-                      "event",
-                      connection->getInputChannelName().c_str());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
+                          "event",
+                          connection->getInputChannelName().c_str());
+                }
                 return; // skip the inconsistent event
             }
 
@@ -3009,6 +3077,7 @@
             break;
         }
         case EventEntry::Type::FOCUS:
+        case EventEntry::Type::TOUCH_MODE_CHANGED:
         case EventEntry::Type::POINTER_CAPTURE_CHANGED:
         case EventEntry::Type::DRAG: {
             break;
@@ -3020,7 +3089,7 @@
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET: {
             LOG_ALWAYS_FATAL("%s events should not go to apps",
-                             NamedEnum::string(newEntry.type).c_str());
+                             ftl::enum_string(newEntry.type).c_str());
             break;
         }
     }
@@ -3114,10 +3183,11 @@
         return;
     }
 
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
-    commandEntry->newToken = token;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, token]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->onPointerDownOutsideFocus(token);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3127,11 +3197,11 @@
                                            connection->getInputChannelName().c_str());
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
+    }
 
-    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
+    while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
         DispatchEntry* dispatchEntry = connection->outboundQueue.front();
         dispatchEntry->deliveryTime = currentTime;
         const std::chrono::nanoseconds timeout =
@@ -3208,9 +3278,7 @@
                                                      motionEntry.xPrecision, motionEntry.yPrecision,
                                                      motionEntry.xCursorPosition,
                                                      motionEntry.yCursorPosition,
-                                                     dispatchEntry->displayOrientation,
-                                                     dispatchEntry->displaySize.x,
-                                                     dispatchEntry->displaySize.y,
+                                                     dispatchEntry->rawTransform,
                                                      motionEntry.downTime, motionEntry.eventTime,
                                                      motionEntry.pointerCount,
                                                      motionEntry.pointerProperties, usingCoords);
@@ -3221,8 +3289,17 @@
                 const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
                 status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
                                                                       focusEntry.id,
-                                                                      focusEntry.hasFocus,
-                                                                      mInTouchMode);
+                                                                      focusEntry.hasFocus);
+                break;
+            }
+
+            case EventEntry::Type::TOUCH_MODE_CHANGED: {
+                const TouchModeEntry& touchModeEntry =
+                        static_cast<const TouchModeEntry&>(eventEntry);
+                status = connection->inputPublisher
+                                 .publishTouchModeEvent(dispatchEntry->seq, touchModeEntry.id,
+                                                        touchModeEntry.inTouchMode);
+
                 break;
             }
 
@@ -3248,7 +3325,7 @@
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::SENSOR: {
                 LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
-                                 NamedEnum::string(eventEntry.type).c_str());
+                                 ftl::enum_string(eventEntry.type).c_str());
                 return;
             }
         }
@@ -3267,11 +3344,11 @@
                 } else {
                     // Pipe is full and we are waiting for the app to finish process some events
                     // before sending more events to it.
-#if DEBUG_DISPATCH_CYCLE
-                    ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
-                          "waiting for the application to catch up",
-                          connection->getInputChannelName().c_str());
-#endif
+                    if (DEBUG_DISPATCH_CYCLE) {
+                        ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
+                              "waiting for the application to catch up",
+                              connection->getInputChannelName().c_str());
+                    }
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -3315,16 +3392,18 @@
 
 const std::array<uint8_t, 32> InputDispatcher::getSignature(
         const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
-    int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
-    if ((actionMasked == AMOTION_EVENT_ACTION_UP) || (actionMasked == AMOTION_EVENT_ACTION_DOWN)) {
+    const int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
+    if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) {
         // Only sign events up and down events as the purely move events
         // are tied to their up/down counterparts so signing would be redundant.
-        VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry);
-        verifiedEvent.actionMasked = actionMasked;
-        verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
-        return sign(verifiedEvent);
+        return INVALID_HMAC;
     }
-    return INVALID_HMAC;
+
+    VerifiedMotionEvent verifiedEvent =
+            verifiedMotionEventFromMotionEntry(motionEntry, dispatchEntry.rawTransform);
+    verifiedEvent.actionMasked = actionMasked;
+    verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
+    return sign(verifiedEvent);
 }
 
 const std::array<uint8_t, 32> InputDispatcher::getSignature(
@@ -3338,27 +3417,30 @@
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection, uint32_t seq,
                                                 bool handled, nsecs_t consumeTime) {
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
-          connection->getInputChannelName().c_str(), seq, toString(handled));
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
+              connection->getInputChannelName().c_str(), seq, toString(handled));
+    }
 
-    if (connection->status == Connection::STATUS_BROKEN ||
-        connection->status == Connection::STATUS_ZOMBIE) {
+    if (connection->status == Connection::Status::BROKEN ||
+        connection->status == Connection::Status::ZOMBIE) {
         return;
     }
 
     // Notify other system components and prepare to start the next dispatch cycle.
-    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
+    auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
+        doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
                                                      const sp<Connection>& connection,
                                                      bool notify) {
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
-          connection->getInputChannelName().c_str(), toString(notify));
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
+              connection->getInputChannelName().c_str(), toString(notify));
+    }
 
     // Clear the dispatch queues.
     drainDispatchQueue(connection->outboundQueue);
@@ -3368,12 +3450,19 @@
 
     // The connection appears to be unrecoverably broken.
     // Ignore already broken or zombie connections.
-    if (connection->status == Connection::STATUS_NORMAL) {
-        connection->status = Connection::STATUS_BROKEN;
+    if (connection->status == Connection::Status::NORMAL) {
+        connection->status = Connection::Status::BROKEN;
 
         if (notify) {
             // Notify other system components.
-            onDispatchCycleBrokenLocked(currentTime, connection);
+            ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
+                  connection->getInputChannelName().c_str());
+
+            auto command = [this, connection]() REQUIRES(mLock) {
+                scoped_unlock unlock(mLock);
+                mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
+            };
+            postCommandLocked(std::move(command));
         }
     }
 }
@@ -3440,7 +3529,7 @@
             gotOne = true;
         }
         if (gotOne) {
-            runCommandsLockedInterruptible();
+            runCommandsLockedInterruptable();
             if (status == WOULD_BLOCK) {
                 return 1;
             }
@@ -3505,7 +3594,7 @@
 
 void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
         const sp<Connection>& connection, const CancelationOptions& options) {
-    if (connection->status == Connection::STATUS_BROKEN) {
+    if (connection->status == Connection::Status::BROKEN) {
         return;
     }
 
@@ -3517,12 +3606,16 @@
     if (cancelationEvents.empty()) {
         return;
     }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
-          "with reality: %s, mode=%d.",
-          connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
-          options.mode);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
+              "with reality: %s, mode=%d.",
+              connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+              options.mode);
+    }
+
+    std::string reason = std::string("reason=").append(options.reason);
+    android_log_event_list(LOGTAG_INPUT_CANCEL)
+            << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
 
     InputTarget target;
     sp<WindowInfoHandle> windowHandle =
@@ -3549,17 +3642,18 @@
                 break;
             }
             case EventEntry::Type::FOCUS:
+            case EventEntry::Type::TOUCH_MODE_CHANGED:
             case EventEntry::Type::POINTER_CAPTURE_CHANGED:
             case EventEntry::Type::DRAG: {
                 LOG_ALWAYS_FATAL("Canceling %s events is not supported",
-                                 NamedEnum::string(cancelationEventEntry->type).c_str());
+                                 ftl::enum_string(cancelationEventEntry->type).c_str());
                 break;
             }
             case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::SENSOR: {
                 LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
-                                 NamedEnum::string(cancelationEventEntry->type).c_str());
+                                 ftl::enum_string(cancelationEventEntry->type).c_str());
                 break;
             }
         }
@@ -3573,7 +3667,7 @@
 
 void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
         const sp<Connection>& connection) {
-    if (connection->status == Connection::STATUS_BROKEN) {
+    if (connection->status == Connection::Status::BROKEN) {
         return;
     }
 
@@ -3586,10 +3680,10 @@
         return;
     }
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
         ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
               connection->getInputChannelName().c_str(), downEvents.size());
-#endif
+    }
 
     InputTarget target;
     sp<WindowInfoHandle> windowHandle =
@@ -3612,13 +3706,14 @@
 
             case EventEntry::Type::KEY:
             case EventEntry::Type::FOCUS:
+            case EventEntry::Type::TOUCH_MODE_CHANGED:
             case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::POINTER_CAPTURE_CHANGED:
             case EventEntry::Type::SENSOR:
             case EventEntry::Type::DRAG: {
                 LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
-                                 NamedEnum::string(downEventEntry->type).c_str());
+                                 ftl::enum_string(downEventEntry->type).c_str());
                 break;
             }
         }
@@ -3721,7 +3816,7 @@
                                           originalMotionEntry.xCursorPosition,
                                           originalMotionEntry.yCursorPosition,
                                           originalMotionEntry.downTime, splitPointerCount,
-                                          splitPointerProperties, splitPointerCoords, 0, 0);
+                                          splitPointerProperties, splitPointerCoords);
 
     if (originalMotionEntry.injectionState) {
         splitMotionEntry->injectionState = originalMotionEntry.injectionState;
@@ -3732,11 +3827,11 @@
 }
 
 void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
+    }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -3788,14 +3883,14 @@
 }
 
 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
-          "policyFlags=0x%x, action=0x%x, "
-          "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
-          args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
-          args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
-          args->downTime);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+              "policyFlags=0x%x, action=0x%x, "
+              "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
+              args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
+              args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
+              args->downTime);
+    }
     if (!validateKeyEvent(args->action)) {
         return;
     }
@@ -3831,7 +3926,7 @@
               std::to_string(t.duration().count()).c_str());
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         mLock.lock();
 
@@ -3866,33 +3961,33 @@
 }
 
 void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
-          "displayId=%" PRId32 ", policyFlags=0x%x, "
-          "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
-          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
-          "yCursorPosition=%f, downTime=%" PRId64,
-          args->id, args->eventTime, args->deviceId, args->source, args->displayId,
-          args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
-          args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
-          args->xCursorPosition, args->yCursorPosition, args->downTime);
-    for (uint32_t i = 0; i < args->pointerCount; i++) {
-        ALOGD("  Pointer %d: id=%d, toolType=%d, "
-              "x=%f, y=%f, pressure=%f, size=%f, "
-              "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
-              "orientation=%f",
-              i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+              "displayId=%" PRId32 ", policyFlags=0x%x, "
+              "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
+              "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
+              "yCursorPosition=%f, downTime=%" PRId64,
+              args->id, args->eventTime, args->deviceId, args->source, args->displayId,
+              args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
+              args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
+              args->xCursorPosition, args->yCursorPosition, args->downTime);
+        for (uint32_t i = 0; i < args->pointerCount; i++) {
+            ALOGD("  Pointer %d: id=%d, toolType=%d, "
+                  "x=%f, y=%f, pressure=%f, size=%f, "
+                  "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+                  "orientation=%f",
+                  i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+        }
     }
-#endif
     if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
                              args->pointerProperties)) {
         return;
@@ -3908,22 +4003,26 @@
               std::to_string(t.duration().count()).c_str());
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         mLock.lock();
 
         if (shouldSendMotionToInputFilterLocked(args)) {
+            ui::Transform displayTransform;
+            if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) {
+                displayTransform = it->second.transform;
+            }
+
             mLock.unlock();
 
             MotionEvent event;
-            ui::Transform transform;
             event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
                              args->action, args->actionButton, args->flags, args->edgeFlags,
-                             args->metaState, args->buttonState, args->classification, transform,
-                             args->xPrecision, args->yPrecision, args->xCursorPosition,
-                             args->yCursorPosition, ui::Transform::ROT_0, INVALID_DISPLAY_SIZE,
-                             INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
-                             args->pointerCount, args->pointerProperties, args->pointerCoords);
+                             args->metaState, args->buttonState, args->classification,
+                             displayTransform, args->xPrecision, args->yPrecision,
+                             args->xCursorPosition, args->yCursorPosition, displayTransform,
+                             args->downTime, args->eventTime, args->pointerCount,
+                             args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
             if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -3943,7 +4042,7 @@
                                               args->xPrecision, args->yPrecision,
                                               args->xCursorPosition, args->yCursorPosition,
                                               args->downTime, args->pointerCount,
-                                              args->pointerProperties, args->pointerCoords, 0, 0);
+                                              args->pointerProperties, args->pointerCoords);
 
         if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
             IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
@@ -3962,14 +4061,14 @@
 }
 
 void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
-          " sensorType=%s",
-          args->id, args->eventTime, args->deviceId, args->source,
-          NamedEnum::string(args->sensorType).c_str());
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+              " sensorType=%s",
+              args->id, args->eventTime, args->deviceId, args->source,
+              ftl::enum_string(args->sensorType).c_str());
+    }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         mLock.lock();
 
@@ -3990,10 +4089,10 @@
 }
 
 void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args->eventTime,
-          args->deviceId, args->isOn);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args->eventTime,
+              args->deviceId, args->isOn);
+    }
     mPolicy->notifyVibratorState(args->deviceId, args->isOn);
 }
 
@@ -4002,11 +4101,11 @@
 }
 
 void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
-          "switchMask=0x%08x",
-          args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
+              "switchMask=0x%08x",
+              args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
+    }
 
     uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4014,12 +4113,12 @@
 }
 
 void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
-          args->deviceId);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
+              args->deviceId);
+    }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -4034,12 +4133,12 @@
 }
 
 void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
-          args->request.enable ? "true" : "false");
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
+              args->request.enable ? "true" : "false");
+    }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         std::scoped_lock _l(mLock);
         auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
@@ -4055,11 +4154,11 @@
 InputEventInjectionResult InputDispatcher::injectInputEvent(
         const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
         InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-          "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
-          event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+              "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
+              event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
+    }
     nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
 
     policyFlags |= POLICY_FLAG_INJECTED;
@@ -4128,12 +4227,17 @@
 
         case AINPUT_EVENT_TYPE_MOTION: {
             const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
-            int32_t action = motionEvent.getAction();
-            size_t pointerCount = motionEvent.getPointerCount();
+            const int32_t action = motionEvent.getAction();
+            const bool isPointerEvent =
+                    isFromSource(event->getSource(), AINPUT_SOURCE_CLASS_POINTER);
+            // If a pointer event has no displayId specified, inject it to the default display.
+            const uint32_t displayId = isPointerEvent && (event->getDisplayId() == ADISPLAY_ID_NONE)
+                    ? ADISPLAY_ID_DEFAULT
+                    : event->getDisplayId();
+            const size_t pointerCount = motionEvent.getPointerCount();
             const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
-            int32_t actionButton = motionEvent.getActionButton();
+            const int32_t actionButton = motionEvent.getActionButton();
             int32_t flags = motionEvent.getFlags();
-            int32_t displayId = motionEvent.getDisplayId();
             if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
                 return InputEventInjectionResult::FAILED;
             }
@@ -4158,8 +4262,8 @@
             std::unique_ptr<MotionEntry> injectedEntry =
                     std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
                                                   resolvedDeviceId, motionEvent.getSource(),
-                                                  motionEvent.getDisplayId(), policyFlags, action,
-                                                  actionButton, flags, motionEvent.getMetaState(),
+                                                  displayId, policyFlags, action, actionButton,
+                                                  flags, motionEvent.getMetaState(),
                                                   motionEvent.getButtonState(),
                                                   motionEvent.getClassification(),
                                                   motionEvent.getEdgeFlags(),
@@ -4168,9 +4272,8 @@
                                                   motionEvent.getRawXCursorPosition(),
                                                   motionEvent.getRawYCursorPosition(),
                                                   motionEvent.getDownTime(), uint32_t(pointerCount),
-                                                  pointerProperties, samplePointerCoords,
-                                                  motionEvent.getXOffset(),
-                                                  motionEvent.getYOffset());
+                                                  pointerProperties, samplePointerCoords);
+            transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
             injectedEntries.push(std::move(injectedEntry));
             for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
                 sampleEventTimes += 1;
@@ -4178,9 +4281,8 @@
                 std::unique_ptr<MotionEntry> nextInjectedEntry =
                         std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
                                                       resolvedDeviceId, motionEvent.getSource(),
-                                                      motionEvent.getDisplayId(), policyFlags,
-                                                      action, actionButton, flags,
-                                                      motionEvent.getMetaState(),
+                                                      displayId, policyFlags, action, actionButton,
+                                                      flags, motionEvent.getMetaState(),
                                                       motionEvent.getButtonState(),
                                                       motionEvent.getClassification(),
                                                       motionEvent.getEdgeFlags(),
@@ -4190,8 +4292,9 @@
                                                       motionEvent.getRawYCursorPosition(),
                                                       motionEvent.getDownTime(),
                                                       uint32_t(pointerCount), pointerProperties,
-                                                      samplePointerCoords, motionEvent.getXOffset(),
-                                                      motionEvent.getYOffset());
+                                                      samplePointerCoords);
+                transformMotionEntryForInjectionLocked(*nextInjectedEntry,
+                                                       motionEvent.getTransform());
                 injectedEntries.push(std::move(nextInjectedEntry));
             }
             break;
@@ -4237,10 +4340,10 @@
 
                 nsecs_t remainingTimeout = endTime - now();
                 if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
-                    ALOGD("injectInputEvent - Timed out waiting for injection result "
-                          "to become available.");
-#endif
+                    if (DEBUG_INJECTION) {
+                        ALOGD("injectInputEvent - Timed out waiting for injection result "
+                              "to become available.");
+                    }
                     injectionResult = InputEventInjectionResult::TIMED_OUT;
                     break;
                 }
@@ -4251,16 +4354,16 @@
             if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
                 syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
                 while (injectionState->pendingForegroundDispatches != 0) {
-#if DEBUG_INJECTION
-                    ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
-                          injectionState->pendingForegroundDispatches);
-#endif
+                    if (DEBUG_INJECTION) {
+                        ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+                              injectionState->pendingForegroundDispatches);
+                    }
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
-                        ALOGD("injectInputEvent - Timed out waiting for pending foreground "
-                              "dispatches to finish.");
-#endif
+                        if (DEBUG_INJECTION) {
+                            ALOGD("injectInputEvent - Timed out waiting for pending foreground "
+                                  "dispatches to finish.");
+                        }
                         injectionResult = InputEventInjectionResult::TIMED_OUT;
                         break;
                     }
@@ -4273,10 +4376,10 @@
         injectionState->release();
     } // release lock
 
-#if DEBUG_INJECTION
-    ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
-          injectionResult, injectorPid, injectorUid);
-#endif
+    if (DEBUG_INJECTION) {
+        ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
+              injectionResult, injectorPid, injectorUid);
+    }
 
     return injectionResult;
 }
@@ -4323,11 +4426,11 @@
                                          InputEventInjectionResult injectionResult) {
     InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
-#if DEBUG_INJECTION
-        ALOGD("Setting input event injection result to %d.  "
-              "injectorPid=%d, injectorUid=%d",
-              injectionResult, injectionState->injectorPid, injectionState->injectorUid);
-#endif
+        if (DEBUG_INJECTION) {
+            ALOGD("Setting input event injection result to %d.  "
+                  "injectorPid=%d, injectorUid=%d",
+                  injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+        }
 
         if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
             // Log the outcome since the injector did not wait for the injection result.
@@ -4355,6 +4458,31 @@
     }
 }
 
+void InputDispatcher::transformMotionEntryForInjectionLocked(
+        MotionEntry& entry, const ui::Transform& injectedTransform) const {
+    // Input injection works in the logical display coordinate space, but the input pipeline works
+    // display space, so we need to transform the injected events accordingly.
+    const auto it = mDisplayInfos.find(entry.displayId);
+    if (it == mDisplayInfos.end()) return;
+    const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
+
+    for (uint32_t i = 0; i < entry.pointerCount; i++) {
+        PointerCoords& pc = entry.pointerCoords[i];
+        // Make a copy of the injected coords. We cannot change them in place because some of them
+        // are interdependent (for example, X coordinate might depend on the Y coordinate).
+        PointerCoords injectedCoords = entry.pointerCoords[i];
+
+        BitSet64 bits(injectedCoords.bits);
+        while (!bits.isEmpty()) {
+            const auto axis = static_cast<int32_t>(bits.clearFirstMarkedBit());
+            const float value =
+                    MotionEvent::calculateTransformedAxisValue(axis, entry.source,
+                                                               transformToDisplay, injectedCoords);
+            pc.setAxisValue(axis, value);
+        }
+    }
+}
+
 void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
     InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
@@ -4487,8 +4615,7 @@
     std::vector<sp<WindowInfoHandle>> newHandles;
     for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
         const WindowInfo* info = handle->getInfo();
-        if ((getInputChannelLocked(handle->getToken()) == nullptr &&
-             info->portalToDisplayId == ADISPLAY_ID_NONE)) {
+        if (getInputChannelLocked(handle->getToken()) == nullptr) {
             const bool noInputChannel =
                     info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
             const bool canReceiveInput = !info->flags.test(WindowInfo::Flag::NOT_TOUCHABLE) ||
@@ -4522,6 +4649,7 @@
 
 void InputDispatcher::setInputWindows(
         const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) {
+    // TODO(b/198444055): Remove setInputWindows from InputDispatcher.
     { // acquire lock
         std::scoped_lock _l(mLock);
         for (const auto& [displayId, handles] : handlesPerDisplay) {
@@ -4549,15 +4677,27 @@
         ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
     }
 
-    // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
+    // Check preconditions for new input windows
     for (const sp<WindowInfoHandle>& window : windowInfoHandles) {
-        const bool noInputWindow =
-                window->getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+        const WindowInfo& info = *window->getInfo();
+
+        // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
+        const bool noInputWindow = info.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
         if (noInputWindow && window->getToken() != nullptr) {
             ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
                   window->getName().c_str());
             window->releaseChannel();
         }
+
+        // Ensure all spy windows are trusted overlays
+        LOG_ALWAYS_FATAL_IF(info.isSpy() && !info.trustedOverlay,
+                            "%s has feature SPY, but is not a trusted overlay.",
+                            window->getName().c_str());
+
+        // Ensure all stylus interceptors are trusted overlays
+        LOG_ALWAYS_FATAL_IF(info.interceptsStylus() && !info.trustedOverlay,
+                            "%s has feature INTERCEPTS_STYLUS, but is not a trusted overlay.",
+                            window->getName().c_str());
     }
 
     // Copy old handles for release if they are no longer present.
@@ -4602,6 +4742,20 @@
                     CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                                "touched window was removed");
                     synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
+                    // Since we are about to drop the touch, cancel the events for the wallpaper as
+                    // well.
+                    if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND &&
+                        touchedWindow.windowHandle->getInfo()->hasWallpaper) {
+                        sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
+                        if (wallpaper != nullptr) {
+                            sp<Connection> wallpaperConnection =
+                                    getConnectionLocked(wallpaper->getToken());
+                            if (wallpaperConnection != nullptr) {
+                                synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
+                                                                               options);
+                            }
+                        }
+                    }
                 }
                 state.windows.erase(state.windows.begin() + i);
             } else {
@@ -4618,21 +4772,19 @@
         }
     }
 
-    if (isPerWindowInputRotationEnabled()) {
-        // Determine if the orientation of any of the input windows have changed, and cancel all
-        // pointer events if necessary.
-        for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
-            const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
-            if (newWindowHandle != nullptr &&
-                newWindowHandle->getInfo()->transform.getOrientation() !=
-                        oldWindowOrientations[oldWindowHandle->getId()]) {
-                std::shared_ptr<InputChannel> inputChannel =
-                        getInputChannelLocked(newWindowHandle->getToken());
-                if (inputChannel != nullptr) {
-                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
-                                               "touched window's orientation changed");
-                    synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
-                }
+    // Determine if the orientation of any of the input windows have changed, and cancel all
+    // pointer events if necessary.
+    for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
+        const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
+        if (newWindowHandle != nullptr &&
+            newWindowHandle->getInfo()->transform.getOrientation() !=
+                    oldWindowOrientations[oldWindowHandle->getId()]) {
+            std::shared_ptr<InputChannel> inputChannel =
+                    getInputChannelLocked(newWindowHandle->getToken());
+            if (inputChannel != nullptr) {
+                CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+                                           "touched window's orientation changed");
+                synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
             }
         }
     }
@@ -4647,18 +4799,6 @@
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
             }
             oldWindowHandle->releaseChannel();
-            // To avoid making too many calls into the compat framework, only
-            // check for window flags when windows are going away.
-            // TODO(b/157929241) : delete this. This is only needed temporarily
-            // in order to gather some data about the flag usage
-            if (oldWindowHandle->getInfo()->flags.test(WindowInfo::Flag::SLIPPERY)) {
-                ALOGW("%s has FLAG_SLIPPERY. Please report this in b/157929241",
-                      oldWindowHandle->getName().c_str());
-                if (mCompatService != nullptr) {
-                    mCompatService->reportChangeByUid(IInputConstants::BLOCK_FLAG_SLIPPERY,
-                                                      oldWindowHandle->getInfo()->ownerUid);
-                }
-            }
         }
     }
 }
@@ -4733,7 +4873,7 @@
 
             // Find new focused window and validate
             sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
-            notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
+            sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken);
 
             if (newFocusedWindowToken == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
@@ -4810,8 +4950,29 @@
 }
 
 void InputDispatcher::setInTouchMode(bool inTouchMode) {
-    std::scoped_lock lock(mLock);
-    mInTouchMode = inTouchMode;
+    bool needWake = false;
+    {
+        std::scoped_lock lock(mLock);
+        if (mInTouchMode == inTouchMode) {
+            return;
+        }
+        if (DEBUG_TOUCH_MODE) {
+            ALOGD("Request to change touch mode from %s to %s", toString(mInTouchMode),
+                  toString(inTouchMode));
+            // TODO(b/198487159): Also print the current last interacted apps.
+        }
+
+        // TODO(b/198499018): Store touch mode per display.
+        mInTouchMode = inTouchMode;
+
+        // TODO(b/198487159): Enforce that only last interacted apps can change touch mode.
+        auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode);
+        needWake = enqueueInboundEventLocked(std::move(entry));
+    } // release lock
+
+    if (needWake) {
+        mLooper->wake();
+    }
 }
 
 void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
@@ -5030,14 +5191,6 @@
             } else {
                 dump += INDENT3 "Windows: <none>\n";
             }
-            if (!state.portalWindows.empty()) {
-                dump += INDENT3 "Portal windows:\n";
-                for (size_t i = 0; i < state.portalWindows.size(); i++) {
-                    const sp<WindowInfoHandle> portalWindowHandle = state.portalWindows[i];
-                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i,
-                                         portalWindowHandle->getName().c_str());
-                }
-            }
         }
     } else {
         dump += INDENT "TouchStates: <no displays touched>\n";
@@ -5049,9 +5202,17 @@
     }
 
     if (!mWindowHandlesByDisplay.empty()) {
-        for (auto& it : mWindowHandlesByDisplay) {
-            const std::vector<sp<WindowInfoHandle>> windowHandles = it.second;
-            dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
+        for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
+            dump += StringPrintf(INDENT "Display: %" PRId32 "\n", displayId);
+            if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+                const auto& displayInfo = it->second;
+                dump += StringPrintf(INDENT2 "logicalSize=%dx%d\n", displayInfo.logicalWidth,
+                                     displayInfo.logicalHeight);
+                displayInfo.transform.dump(dump, "transform", INDENT4);
+            } else {
+                dump += INDENT2 "No DisplayInfo found!\n";
+            }
+
             if (!windowHandles.empty()) {
                 dump += INDENT2 "Windows:\n";
                 for (size_t i = 0; i < windowHandles.size(); i++) {
@@ -5059,7 +5220,7 @@
                     const WindowInfo* windowInfo = windowHandle->getInfo();
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
-                                                 "portalToDisplayId=%d, paused=%s, focusable=%s, "
+                                                 "paused=%s, focusable=%s, "
                                                  "hasWallpaper=%s, visible=%s, alpha=%.2f, "
                                                  "flags=%s, type=%s, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
@@ -5067,13 +5228,12 @@
                                                  "applicationInfo.token=%s, "
                                                  "touchableRegion=",
                                          i, windowInfo->name.c_str(), windowInfo->id,
-                                         windowInfo->displayId, windowInfo->portalToDisplayId,
-                                         toString(windowInfo->paused),
+                                         windowInfo->displayId, toString(windowInfo->paused),
                                          toString(windowInfo->focusable),
                                          toString(windowInfo->hasWallpaper),
                                          toString(windowInfo->visible), windowInfo->alpha,
                                          windowInfo->flags.string().c_str(),
-                                         NamedEnum::string(windowInfo->type).c_str(),
+                                         ftl::enum_string(windowInfo->type).c_str(),
                                          windowInfo->frameLeft, windowInfo->frameTop,
                                          windowInfo->frameRight, windowInfo->frameBottom,
                                          windowInfo->globalScaleFactor,
@@ -5084,13 +5244,12 @@
                                          windowInfo->inputFeatures.string().c_str());
                     dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
                                          "ms, trustedOverlay=%s, hasToken=%s, "
-                                         "touchOcclusionMode=%s, displayOrientation=%d\n",
+                                         "touchOcclusionMode=%s\n",
                                          windowInfo->ownerPid, windowInfo->ownerUid,
                                          millis(windowInfo->dispatchingTimeout),
                                          toString(windowInfo->trustedOverlay),
                                          toString(windowInfo->token != nullptr),
-                                         toString(windowInfo->touchOcclusionMode).c_str(),
-                                         windowInfo->displayOrientation);
+                                         toString(windowInfo->touchOcclusionMode).c_str());
                     windowInfo->transform.dump(dump, "transform", INDENT4);
                 }
             } else {
@@ -5165,6 +5324,12 @@
         dump += INDENT "ReplacedKeys: <empty>\n";
     }
 
+    if (!mCommandQueue.empty()) {
+        dump += StringPrintf(INDENT "CommandQueue: size=%zu\n", mCommandQueue.size());
+    } else {
+        dump += INDENT "CommandQueue: <empty>\n";
+    }
+
     if (!mConnectionsByToken.empty()) {
         dump += INDENT "Connections:\n";
         for (const auto& [token, connection] : mConnectionsByToken) {
@@ -5172,7 +5337,8 @@
                                          "status=%s, monitor=%s, responsive=%s\n",
                                  connection->inputChannel->getFd().get(),
                                  connection->getInputChannelName().c_str(),
-                                 connection->getWindowName().c_str(), connection->getStatusLabel(),
+                                 connection->getWindowName().c_str(),
+                                 ftl::enum_string(connection->status).c_str(),
                                  toString(connection->monitor), toString(connection->responsive));
 
             if (!connection->outboundQueue.empty()) {
@@ -5231,9 +5397,9 @@
 };
 
 Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
-#if DEBUG_CHANNEL_CREATION
-    ALOGD("channel '%s' ~ createInputChannel", name.c_str());
-#endif
+    if (DEBUG_CHANNEL_CREATION) {
+        ALOGD("channel '%s' ~ createInputChannel", name.c_str());
+    }
 
     std::unique_ptr<InputChannel> serverChannel;
     std::unique_ptr<InputChannel> clientChannel;
@@ -5345,7 +5511,7 @@
     nsecs_t currentTime = now();
     abortBrokenDispatchCycleLocked(currentTime, connection, notify);
 
-    connection->status = Connection::STATUS_ZOMBIE;
+    connection->status = Connection::Status::ZOMBIE;
     return OK;
 }
 
@@ -5379,58 +5545,78 @@
 status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
     { // acquire lock
         std::scoped_lock _l(mLock);
-        std::optional<int32_t> foundDisplayId = findGestureMonitorDisplayByTokenLocked(token);
 
-        if (!foundDisplayId) {
-            ALOGW("Attempted to pilfer pointers from an un-registered monitor or invalid token");
-            return BAD_VALUE;
-        }
-        int32_t displayId = foundDisplayId.value();
-
-        std::unordered_map<int32_t, TouchState>::iterator stateIt =
-                mTouchStatesByDisplay.find(displayId);
-        if (stateIt == mTouchStatesByDisplay.end()) {
-            ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
-            return BAD_VALUE;
-        }
-
-        TouchState& state = stateIt->second;
+        TouchState* statePtr = nullptr;
         std::shared_ptr<InputChannel> requestingChannel;
-        std::optional<int32_t> foundDeviceId;
-        for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
-            if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
-                requestingChannel = touchedMonitor.monitor.inputChannel;
-                foundDeviceId = state.deviceId;
+        int32_t displayId;
+        int32_t deviceId;
+        const std::optional<int32_t> foundGestureMonitorDisplayId =
+                findGestureMonitorDisplayByTokenLocked(token);
+
+        // TODO: Optimize this function for pilfering from windows when removing gesture monitors.
+        if (foundGestureMonitorDisplayId) {
+            // A gesture monitor has requested to pilfer pointers.
+            displayId = *foundGestureMonitorDisplayId;
+            auto stateIt = mTouchStatesByDisplay.find(displayId);
+            if (stateIt == mTouchStatesByDisplay.end()) {
+                ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
+                return BAD_VALUE;
+            }
+            statePtr = &stateIt->second;
+
+            for (const auto& monitor : statePtr->gestureMonitors) {
+                if (monitor.inputChannel->getConnectionToken() == token) {
+                    requestingChannel = monitor.inputChannel;
+                    deviceId = statePtr->deviceId;
+                }
+            }
+        } else {
+            // Check if a window has requested to pilfer pointers.
+            for (auto& [curDisplayId, state] : mTouchStatesByDisplay) {
+                const sp<WindowInfoHandle>& windowHandle = state.getWindow(token);
+                if (windowHandle != nullptr) {
+                    displayId = curDisplayId;
+                    requestingChannel = getInputChannelLocked(token);
+                    deviceId = state.deviceId;
+                    statePtr = &state;
+                    break;
+                }
             }
         }
-        if (!foundDeviceId || !state.down) {
-            ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams."
+
+        if (requestingChannel == nullptr) {
+            ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token");
+            return BAD_VALUE;
+        }
+        TouchState& state = *statePtr;
+        if (!state.down) {
+            ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
                   " Ignoring.");
             return BAD_VALUE;
         }
-        int32_t deviceId = foundDeviceId.value();
 
         // Send cancel events to all the input channels we're stealing from.
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
-                                   "gesture monitor stole pointer stream");
+                                   "input channel stole pointer stream");
         options.deviceId = deviceId;
         options.displayId = displayId;
-        std::string canceledWindows = "[";
+        std::string canceledWindows;
         for (const TouchedWindow& window : state.windows) {
             std::shared_ptr<InputChannel> channel =
                     getInputChannelLocked(window.windowHandle->getToken());
-            if (channel != nullptr) {
+            if (channel != nullptr && channel->getConnectionToken() != token) {
                 synthesizeCancelationEventsForInputChannelLocked(channel, options);
-                canceledWindows += channel->getName() + ", ";
+                canceledWindows += canceledWindows.empty() ? "[" : ", ";
+                canceledWindows += channel->getName();
             }
         }
-        canceledWindows += "]";
-        ALOGI("Monitor %s is stealing touch from %s", requestingChannel->getName().c_str(),
+        canceledWindows += canceledWindows.empty() ? "[]" : "]";
+        ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(),
               canceledWindows.c_str());
 
         // Then clear the current touch state so we stop dispatching to them as well.
         state.split = false;
-        state.filterNonMonitors();
+        state.filterWindowsExcept(token);
     }
     return OK;
 }
@@ -5514,46 +5700,92 @@
     mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
 }
 
-void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
-                                                    const sp<Connection>& connection, uint32_t seq,
-                                                    bool handled, nsecs_t consumeTime) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
-    commandEntry->connection = connection;
-    commandEntry->eventTime = currentTime;
-    commandEntry->seq = seq;
-    commandEntry->handled = handled;
-    commandEntry->consumeTime = consumeTime;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
+                                                     const sp<Connection>& connection, uint32_t seq,
+                                                     bool handled, nsecs_t consumeTime) {
+    // Handle post-event policy actions.
+    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt == connection->waitQueue.end()) {
+        return;
+    }
+    DispatchEntry* dispatchEntry = *dispatchEntryIt;
+    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
+    }
+    if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
+        mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
+                                           connection->inputChannel->getConnectionToken(),
+                                           dispatchEntry->deliveryTime, consumeTime, finishTime);
+    }
+
+    bool restartEvent;
+    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
+        KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
+        restartEvent =
+                afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
+    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
+        MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
+        restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry,
+                                                           handled);
+    } else {
+        restartEvent = false;
+    }
+
+    // Dequeue the event and start the next cycle.
+    // Because the lock might have been released, it is possible that the
+    // contents of the wait queue to have been drained, so we need to double-check
+    // a few things.
+    dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt != connection->waitQueue.end()) {
+        dispatchEntry = *dispatchEntryIt;
+        connection->waitQueue.erase(dispatchEntryIt);
+        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
+        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
+        if (!connection->responsive) {
+            connection->responsive = isConnectionResponsive(*connection);
+            if (connection->responsive) {
+                // The connection was unresponsive, and now it's responsive.
+                processConnectionResponsiveLocked(*connection);
+            }
+        }
+        traceWaitQueueLength(*connection);
+        if (restartEvent && connection->status == Connection::Status::NORMAL) {
+            connection->outboundQueue.push_front(dispatchEntry);
+            traceOutboundQueueLength(*connection);
+        } else {
+            releaseDispatchEntry(dispatchEntry);
+        }
+    }
+
+    // Start the next dispatch cycle for this connection.
+    startDispatchCycleLocked(now(), connection);
 }
 
-void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime,
-                                                  const sp<Connection>& connection) {
-    ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
-          connection->getInputChannelName().c_str());
-
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
-    commandEntry->connection = connection;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken,
+                                                    const sp<IBinder>& newToken) {
+    auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyFocusChanged(oldToken, newToken);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
-                                               const sp<IBinder>& newToken) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
-    commandEntry->oldToken = oldToken;
-    commandEntry->newToken = newToken;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) {
+    auto command = [this, token, x, y]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyDropWindow(token, x, y);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) {
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyDropWindowLockedInterruptible);
-    commandEntry->newToken = token;
-    commandEntry->x = x;
-    commandEntry->y = y;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) {
+    auto command = [this, obscuringPackage]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyUntrustedTouch(obscuringPackage);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
@@ -5596,17 +5828,11 @@
             StringPrintf("%s does not have a focused window", application->getName().c_str());
     updateLastAnrStateLocked(*application, reason);
 
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
-    commandEntry->inputApplicationHandle = std::move(application);
-    postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible);
-    commandEntry->obscuringPackage = obscuringPackage;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyNoFocusedWindowAnr(application);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window,
@@ -5637,109 +5863,24 @@
     dumpDispatchStateLocked(mLastAnrState);
 }
 
-void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
-
-    if (connection->status != Connection::STATUS_ZOMBIE) {
-        mLock.unlock();
-
-        mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
-
-        mLock.lock();
-    }
-}
-
-void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) {
-    sp<IBinder> oldToken = commandEntry->oldToken;
-    sp<IBinder> newToken = commandEntry->newToken;
-    mLock.unlock();
-    mPolicy->notifyFocusChanged(oldToken, newToken);
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) {
-    sp<IBinder> newToken = commandEntry->newToken;
-    mLock.unlock();
-    mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y);
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyWindowResponsive(commandEntry->connectionToken);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyMonitorResponsive(commandEntry->pid);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
-        CommandEntry* commandEntry) {
-    KeyEntry& entry = *(commandEntry->keyEntry);
-    KeyEvent event = createKeyEvent(entry);
-
-    mLock.unlock();
-
-    android::base::Timer t;
-    const sp<IBinder>& token = commandEntry->connectionToken;
-    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags);
-    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
-        ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
-              std::to_string(t.duration().count()).c_str());
-    }
-
-    mLock.lock();
+void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+                                                             KeyEntry& entry) {
+    const KeyEvent event = createKeyEvent(entry);
+    nsecs_t delay = 0;
+    { // release lock
+        scoped_unlock unlock(mLock);
+        android::base::Timer t;
+        delay = mPolicy->interceptKeyBeforeDispatching(focusedWindowToken, &event,
+                                                       entry.policyFlags);
+        if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+            ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
+                  std::to_string(t.duration().count()).c_str());
+        }
+    } // acquire lock
 
     if (delay < 0) {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
-    } else if (!delay) {
+    } else if (delay == 0) {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
     } else {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
@@ -5747,122 +5888,37 @@
     }
 }
 
-void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-    mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
-    mLock.lock();
-}
-
-/**
- * Connection is responsive if it has no events in the waitQueue that are older than the
- * current time.
- */
-static bool isConnectionResponsive(const Connection& connection) {
-    const nsecs_t currentTime = now();
-    for (const DispatchEntry* entry : connection.waitQueue) {
-        if (entry->timeoutTime < currentTime) {
-            return false;
-        }
-    }
-    return true;
-}
-
-void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
-    const nsecs_t finishTime = commandEntry->eventTime;
-    uint32_t seq = commandEntry->seq;
-    const bool handled = commandEntry->handled;
-
-    // Handle post-event policy actions.
-    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
-    if (dispatchEntryIt == connection->waitQueue.end()) {
-        return;
-    }
-    DispatchEntry* dispatchEntry = *dispatchEntryIt;
-    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
-    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
-              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
-    }
-    if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
-        mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
-                                           connection->inputChannel->getConnectionToken(),
-                                           dispatchEntry->deliveryTime, commandEntry->consumeTime,
-                                           finishTime);
-    }
-
-    bool restartEvent;
-    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
-        KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
-        restartEvent =
-                afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
-    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
-        MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
-        restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
-                                                           handled);
-    } else {
-        restartEvent = false;
-    }
-
-    // Dequeue the event and start the next cycle.
-    // Because the lock might have been released, it is possible that the
-    // contents of the wait queue to have been drained, so we need to double-check
-    // a few things.
-    dispatchEntryIt = connection->findWaitQueueEntry(seq);
-    if (dispatchEntryIt != connection->waitQueue.end()) {
-        dispatchEntry = *dispatchEntryIt;
-        connection->waitQueue.erase(dispatchEntryIt);
-        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
-        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
-        if (!connection->responsive) {
-            connection->responsive = isConnectionResponsive(*connection);
-            if (connection->responsive) {
-                // The connection was unresponsive, and now it's responsive.
-                processConnectionResponsiveLocked(*connection);
-            }
-        }
-        traceWaitQueueLength(*connection);
-        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
-            connection->outboundQueue.push_front(dispatchEntry);
-            traceOutboundQueueLength(*connection);
-        } else {
-            releaseDispatchEntry(dispatchEntry);
-        }
-    }
-
-    // Start the next dispatch cycle for this connection.
-    startDispatchCycleLocked(now(), connection);
-}
-
 void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
-    std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible);
-    monitorUnresponsiveCommand->pid = pid;
-    monitorUnresponsiveCommand->reason = std::move(reason);
-    postCommandLocked(std::move(monitorUnresponsiveCommand));
+    auto command = [this, pid, reason = std::move(reason)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyMonitorUnresponsive(pid, reason);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
+void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
                                                           std::string reason) {
-    std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
-    windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
-    windowUnresponsiveCommand->reason = std::move(reason);
-    postCommandLocked(std::move(windowUnresponsiveCommand));
+    auto command = [this, token, reason = std::move(reason)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyWindowUnresponsive(token, reason);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
-    std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible);
-    monitorResponsiveCommand->pid = pid;
-    postCommandLocked(std::move(monitorResponsiveCommand));
+    auto command = [this, pid]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyMonitorResponsive(pid);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) {
-    std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible);
-    windowResponsiveCommand->connectionToken = std::move(connectionToken);
-    postCommandLocked(std::move(windowResponsiveCommand));
+void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) {
+    auto command = [this, connectionToken]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyWindowResponsive(connectionToken);
+    };
+    postCommandLocked(std::move(command));
 }
 
 /**
@@ -5910,7 +5966,7 @@
     sendWindowResponsiveCommandLocked(connectionToken);
 }
 
-bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
                                                        DispatchEntry* dispatchEntry,
                                                        KeyEntry& keyEntry, bool handled) {
     if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
@@ -5935,11 +5991,12 @@
         // then cancel the associated fallback key, if any.
         if (fallbackKeyCode != -1) {
             // Dispatch the unhandled key to the policy with the cancel flag.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
-                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                  keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
+                      "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                      keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount,
+                      keyEntry.policyFlags);
+            }
             KeyEvent event = createKeyEvent(keyEntry);
             event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
 
@@ -5967,21 +6024,21 @@
         // Then ask the policy what to do with it.
         bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
         if (fallbackKeyCode == -1 && !initialDown) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Skipping unhandled key event processing "
-                  "since this is not an initial down.  "
-                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                  originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: Skipping unhandled key event processing "
+                      "since this is not an initial down.  "
+                      "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                      originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
+            }
             return false;
         }
 
         // Dispatch the unhandled key to the policy.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
-              "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-              keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+        if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+            ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
+                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                  keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
+        }
         KeyEvent event = createKeyEvent(keyEntry);
 
         mLock.unlock();
@@ -5992,7 +6049,7 @@
 
         mLock.lock();
 
-        if (connection->status != Connection::STATUS_NORMAL) {
+        if (connection->status != Connection::Status::NORMAL) {
             connection->inputState.removeFallbackKey(originalKeyCode);
             return false;
         }
@@ -6015,19 +6072,19 @@
         // longer dispatch a fallback key to the application.
         if (fallbackKeyCode != AKEYCODE_UNKNOWN &&
             (!fallback || fallbackKeyCode != event.getKeyCode())) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            if (fallback) {
-                ALOGD("Unhandled key event: Policy requested to send key %d"
-                      "as a fallback for %d, but on the DOWN it had requested "
-                      "to send %d instead.  Fallback canceled.",
-                      event.getKeyCode(), originalKeyCode, fallbackKeyCode);
-            } else {
-                ALOGD("Unhandled key event: Policy did not request fallback for %d, "
-                      "but on the DOWN it had requested to send %d.  "
-                      "Fallback canceled.",
-                      originalKeyCode, fallbackKeyCode);
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                if (fallback) {
+                    ALOGD("Unhandled key event: Policy requested to send key %d"
+                          "as a fallback for %d, but on the DOWN it had requested "
+                          "to send %d instead.  Fallback canceled.",
+                          event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+                } else {
+                    ALOGD("Unhandled key event: Policy did not request fallback for %d, "
+                          "but on the DOWN it had requested to send %d.  "
+                          "Fallback canceled.",
+                          originalKeyCode, fallbackKeyCode);
+                }
             }
-#endif
 
             CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
                                        "canceling fallback, policy no longer desires it");
@@ -6041,18 +6098,18 @@
             }
         }
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        {
-            std::string msg;
-            const KeyedVector<int32_t, int32_t>& fallbackKeys =
-                    connection->inputState.getFallbackKeys();
-            for (size_t i = 0; i < fallbackKeys.size(); i++) {
-                msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
+        if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+            {
+                std::string msg;
+                const KeyedVector<int32_t, int32_t>& fallbackKeys =
+                        connection->inputState.getFallbackKeys();
+                for (size_t i = 0; i < fallbackKeys.size(); i++) {
+                    msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
+                }
+                ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
+                      fallbackKeys.size(), msg.c_str());
             }
-            ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
-                  fallbackKeys.size(), msg.c_str());
         }
-#endif
 
         if (fallback) {
             // Restart the dispatch cycle using the fallback key.
@@ -6068,16 +6125,16 @@
             keyEntry.downTime = event.getDownTime();
             keyEntry.syntheticRepeat = false;
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Dispatching fallback key.  "
-                  "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
-                  originalKeyCode, fallbackKeyCode, keyEntry.metaState);
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: Dispatching fallback key.  "
+                      "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+                      originalKeyCode, fallbackKeyCode, keyEntry.metaState);
+            }
             return true; // restart the event
         } else {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: No fallback key.");
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: No fallback key.");
+            }
 
             // Report the key as unhandled, since there is no fallback key.
             mReporter->reportUnhandledKey(keyEntry.id);
@@ -6086,21 +6143,12 @@
     return false;
 }
 
-bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterMotionEventLockedInterruptable(const sp<Connection>& connection,
                                                           DispatchEntry* dispatchEntry,
                                                           MotionEntry& motionEntry, bool handled) {
     return false;
 }
 
-void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
-                              commandEntry->displayId);
-
-    mLock.lock();
-}
-
 void InputDispatcher::traceInboundQueueLengthLocked() {
     if (ATRACE_ENABLED()) {
         ATRACE_INT("iq", mInboundQueue.size());
@@ -6212,7 +6260,7 @@
     disablePointerCaptureForcedLocked();
 
     if (mFocusedDisplayId == changes.displayId) {
-        notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
+        sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
     }
 }
 
@@ -6244,22 +6292,14 @@
     mInboundQueue.push_front(std::move(entry));
 }
 
-void InputDispatcher::setPointerCaptureLocked(bool enabled) {
-    mCurrentPointerCaptureRequest.enable = enabled;
+void InputDispatcher::setPointerCaptureLocked(bool enable) {
+    mCurrentPointerCaptureRequest.enable = enable;
     mCurrentPointerCaptureRequest.seq++;
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doSetPointerCaptureLockedInterruptible);
-    commandEntry->pointerCaptureRequest = mCurrentPointerCaptureRequest;
-    postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::doSetPointerCaptureLockedInterruptible(
-        android::inputdispatcher::CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->setPointerCapture(commandEntry->pointerCaptureRequest);
-
-    mLock.lock();
+    auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->setPointerCapture(request);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::displayRemoved(int32_t displayId) {
@@ -6277,16 +6317,29 @@
     mLooper->wake();
 }
 
-void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) {
+void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+                                           const std::vector<DisplayInfo>& displayInfos) {
     // The listener sends the windows as a flattened array. Separate the windows by display for
     // more convenient parsing.
     std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
-
     for (const auto& info : windowInfos) {
         handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
         handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
     }
-    setInputWindows(handlesPerDisplay);
+
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        mDisplayInfos.clear();
+        for (const auto& displayInfo : displayInfos) {
+            mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
+        }
+
+        for (const auto& [displayId, handles] : handlesPerDisplay) {
+            setInputWindowsLocked(handles, displayId);
+        }
+    }
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
 }
 
 bool InputDispatcher::shouldDropInput(
@@ -6296,7 +6349,7 @@
          isWindowObscuredLocked(windowHandle))) {
         ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
               "%" PRId32 ".",
-              NamedEnum::string(entry.type).c_str(), windowHandle->getName().c_str(),
+              ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
               windowHandle->getInfo()->inputFeatures.string().c_str(),
               windowHandle->getInfo()->displayId);
         return true;
@@ -6304,4 +6357,25 @@
     return false;
 }
 
+void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
+        const std::vector<gui::WindowInfo>& windowInfos,
+        const std::vector<DisplayInfo>& displayInfos) {
+    mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
+}
+
+void InputDispatcher::cancelCurrentTouch() {
+    {
+        std::scoped_lock _l(mLock);
+        ALOGD("Canceling all ongoing pointer gestures on all displays.");
+        CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+                                   "cancel current touch");
+        synthesizeCancelationEventsForAllConnectionsLocked(options);
+
+        mTouchStatesByDisplay.clear();
+        mLastHoverWindowHandle.clear();
+    }
+    // Wake up poll loop since there might be work to do.
+    mLooper->wake();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 04913d4..cbb7bad 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -36,7 +36,6 @@
 #include "TouchedWindow.h"
 
 #include <attestation/HmacKeyManager.h>
-#include <com/android/internal/compat/IPlatformCompatNative.h>
 #include <gui/InputApplication.h>
 #include <gui/WindowInfo.h>
 #include <input/Input.h>
@@ -47,7 +46,6 @@
 #include <unistd.h>
 #include <utils/BitSet.h>
 #include <utils/Looper.h>
-#include <utils/RefBase.h>
 #include <utils/Timers.h>
 #include <utils/threads.h>
 #include <condition_variable>
@@ -81,12 +79,12 @@
  *
  *     A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
  */
-class InputDispatcher : public android::InputDispatcherInterface, public gui::WindowInfosListener {
-protected:
-    ~InputDispatcher() override;
-
+class InputDispatcher : public android::InputDispatcherInterface {
 public:
+    static constexpr bool kDefaultInTouchMode = true;
+
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+    ~InputDispatcher() override;
 
     void dump(std::string& dump) override;
     void monitor() override;
@@ -143,7 +141,11 @@
 
     void displayRemoved(int32_t displayId) override;
 
-    void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override;
+    // Public because it's also used by tests to simulate the WindowInfosListener callback
+    void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos,
+                              const std::vector<android::gui::DisplayInfo>& displayInfos);
+
+    void cancelCurrentTouch() override;
 
 private:
     enum class DropReason {
@@ -171,7 +173,26 @@
     std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
     std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
     std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
-    std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
+
+    // A command entry captures state and behavior for an action to be performed in the
+    // dispatch loop after the initial processing has taken place.  It is essentially
+    // a kind of continuation used to postpone sensitive policy interactions to a point
+    // in the dispatch loop where it is safe to release the lock (generally after finishing
+    // the critical parts of the dispatch cycle).
+    //
+    // The special thing about commands is that they can voluntarily release and reacquire
+    // the dispatcher lock at will.  Initially when the command starts running, the
+    // dispatcher lock is held.  However, if the command needs to call into the policy to
+    // do some work, it can release the lock, do the work, then reacquire the lock again
+    // before returning.
+    //
+    // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+    // never calls into the policy while holding its lock.
+    //
+    // Commands are called with the lock held, but they can release and re-acquire the lock from
+    // within.
+    using Command = std::function<void()>;
+    std::deque<Command> mCommandQueue GUARDED_BY(mLock);
 
     DropReason mLastDropReason GUARDED_BY(mLock);
 
@@ -212,12 +233,12 @@
     // to transfer focus to a new application.
     std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
-    sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x,
-                                                                 int32_t y, TouchState* touchState,
-                                                                 bool addOutsideTargets = false,
-                                                                 bool addPortalWindows = false,
-                                                                 bool ignoreDragWindow = false)
-            REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
+            int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
+            bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
+
+    std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
+            int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
 
     sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
             REQUIRES(mLock);
@@ -261,6 +282,9 @@
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
     void setInjectionResult(EventEntry& entry,
                             android::os::InputEventInjectionResult injectionResult);
+    void transformMotionEntryForInjectionLocked(MotionEntry&,
+                                                const ui::Transform& injectedTransform) const
+            REQUIRES(mLock);
 
     std::condition_variable mInjectionSyncFinished;
     void incrementPendingForegroundDispatches(EventEntry& entry);
@@ -296,8 +320,8 @@
 
     // Deferred command processing.
     bool haveCommandsLocked() const REQUIRES(mLock);
-    bool runCommandsLockedInterruptible() REQUIRES(mLock);
-    void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+    bool runCommandsLockedInterruptable() REQUIRES(mLock);
+    void postCommandLocked(Command&& command) REQUIRES(mLock);
 
     nsecs_t processAnrsLocked() REQUIRES(mLock);
     std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
@@ -319,8 +343,22 @@
     float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
     android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
 
-    std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>
+    class DispatcherWindowListener : public gui::WindowInfosListener {
+    public:
+        explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){};
+        void onWindowInfosChanged(
+                const std::vector<android::gui::WindowInfo>& windowInfos,
+                const std::vector<android::gui::DisplayInfo>& displayInfos) override;
+
+    private:
+        InputDispatcher& mDispatcher;
+    };
+    sp<gui::WindowInfosListener> mWindowInfoListener;
+
+    std::unordered_map<int32_t /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>>
             mWindowHandlesByDisplay GUARDED_BY(mLock);
+    std::unordered_map<int32_t /*displayId*/, android::gui::DisplayInfo> mDisplayInfos
+            GUARDED_BY(mLock);
     void setInputWindowsLocked(
             const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
             int32_t displayId) REQUIRES(mLock);
@@ -343,6 +381,12 @@
     bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const
             REQUIRES(mLock);
 
+    // Gets all the input targets (with their respective input channels) from the window handles
+    // passed as argument.
+    std::vector<InputTarget> getInputTargetsFromWindowHandlesLocked(
+            const std::vector<sp<android::gui::WindowInfoHandle>>& windowHandles) const
+            REQUIRES(mLock);
+
     /*
      * Validate and update InputWindowHandles for a given display.
      */
@@ -405,9 +449,12 @@
     void dispatchPointerCaptureChangedLocked(
             nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
             DropReason& dropReason) REQUIRES(mLock);
+    void dispatchTouchModeChangeLocked(nsecs_t currentTime,
+                                       const std::shared_ptr<TouchModeEntry>& entry)
+            REQUIRES(mLock);
     void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-    void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+    void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry,
                               DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
     void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
     void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
@@ -454,24 +501,11 @@
      */
     void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
 
-    /**
-     * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command.
-     */
     void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
-    /**
-     * Post `doNotifyWindowUnresponsiveLockedInterruptible` command.
-     */
-    void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason)
+    void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken, std::string reason)
             REQUIRES(mLock);
-    /**
-     * Post `doNotifyMonitorResponsiveLockedInterruptible` command.
-     */
     void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
-    /**
-     * Post `doNotifyWindowResponsiveLockedInterruptible` command.
-     */
-    void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
-
+    void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
 
     // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
     // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -499,20 +533,16 @@
     android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
             nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
             nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
-    std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
-            int32_t displayId,
-            const std::vector<sp<android::gui::WindowInfoHandle>>& portalWindows) const
-            REQUIRES(mLock);
-    std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
-            const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
+    std::vector<Monitor> selectResponsiveMonitorsLocked(
+            const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
 
     void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
                                int32_t targetFlags, BitSet32 pointerIds,
                                std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-    void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
+    void addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId,
                                    std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-    void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
-                                          float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
+    void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
+            REQUIRES(mLock);
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
     bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle,
                                   const InjectionState* injectionState);
@@ -611,53 +641,30 @@
             REQUIRES(mLock);
 
     // Interesting events that we might like to log or tell the framework about.
-    void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                       uint32_t seq, bool handled, nsecs_t consumeTime)
+    void doDispatchCycleFinishedCommand(nsecs_t finishTime, const sp<Connection>& connection,
+                                        uint32_t seq, bool handled, nsecs_t consumeTime)
             REQUIRES(mLock);
-    void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
-            REQUIRES(mLock);
+    void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+                                                KeyEntry& entry) REQUIRES(mLock);
     void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
-    void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+    void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
             REQUIRES(mLock);
-    void notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+    void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+    void sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
-    void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const InputApplicationHandle& application,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
             REQUIRES(mLock);
-
-    // Outbound policy interactions.
-    void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-
-    // ANR-related callbacks - start
-    void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    // ANR-related callbacks - end
-    void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+    bool afterKeyEventLockedInterruptable(const sp<Connection>& connection,
                                           DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
                                           bool handled) REQUIRES(mLock);
-    bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+    bool afterMotionEventLockedInterruptable(const sp<Connection>& connection,
                                              DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
                                              bool handled) REQUIRES(mLock);
-    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
     // Find touched state and touched window by token.
     std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
@@ -671,9 +678,6 @@
     void traceWaitQueueLength(const Connection& connection);
 
     sp<InputReporterInterface> mReporter;
-    sp<com::android::internal::compat::IPlatformCompatNative> mCompatService;
-
-    void onFirstRef() override;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
index 8d7fa75..bca1600 100644
--- a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
@@ -19,9 +19,9 @@
 
 namespace android {
 
-sp<InputDispatcherInterface> createInputDispatcher(
+std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
         const sp<InputDispatcherPolicyInterface>& policy) {
-    return new android::inputdispatcher::InputDispatcher(policy);
+    return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);
 }
 
 } // namespace android
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 3bb0bc9..ad3c615 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -296,8 +296,7 @@
                                                   memento.yPrecision, memento.xCursorPosition,
                                                   memento.yCursorPosition, memento.downTime,
                                                   memento.pointerCount, memento.pointerProperties,
-                                                  memento.pointerCoords, 0 /*xOffset*/,
-                                                  0 /*yOffset*/));
+                                                  memento.pointerCoords));
         }
     }
     return events;
@@ -349,8 +348,7 @@
                                                   AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                                   memento.yPrecision, memento.xCursorPosition,
                                                   memento.yCursorPosition, memento.downTime,
-                                                  pointerCount, pointerProperties, pointerCoords,
-                                                  0 /*xOffset*/, 0 /*yOffset*/));
+                                                  pointerCount, pointerProperties, pointerCoords));
         }
 
         memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 7c463c8..0725389 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -21,7 +21,6 @@
 #include <input/InputTransport.h>
 #include <ui/Transform.h>
 #include <utils/BitSet.h>
-#include <utils/RefBase.h>
 
 namespace android::inputdispatcher {
 
@@ -101,11 +100,8 @@
     // (ignored for KeyEvents)
     float globalScaleFactor = 1.0f;
 
-    // Current display orientation
-    uint32_t displayOrientation = ui::Transform::ROT_0;
-
-    // Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
-    int2 displaySize = {INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE};
+    // Current display transform. Used for compatibility for raw coordinates.
+    ui::Transform displayTransform;
 
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index bbce759..43a82d5 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -22,8 +22,4 @@
 Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid)
       : inputChannel(inputChannel), pid(pid) {}
 
-// --- TouchedMonitor ---
-TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
-      : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {}
-
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index 7be0760..365d5be 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -29,15 +29,6 @@
     explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid);
 };
 
-// For tracking the offsets we need to apply when adding gesture monitor targets.
-struct TouchedMonitor {
-    Monitor monitor;
-    float xOffset = 0.f;
-    float yOffset = 0.f;
-
-    explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
-};
-
 } // namespace android::inputdispatcher
 
 #endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 20b6ead..08c7826 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -37,7 +37,6 @@
     source = 0;
     displayId = ADISPLAY_ID_NONE;
     windows.clear();
-    portalWindows.clear();
     gestureMonitors.clear();
 }
 
@@ -48,7 +47,6 @@
     source = other.source;
     displayId = other.displayId;
     windows = other.windows;
-    portalWindows = other.portalWindows;
     gestureMonitors = other.gestureMonitors;
 }
 
@@ -77,17 +75,7 @@
     windows.push_back(touchedWindow);
 }
 
-void TouchState::addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle) {
-    size_t numWindows = portalWindows.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        if (portalWindows[i] == windowHandle) {
-            return;
-        }
-    }
-    portalWindows.push_back(windowHandle);
-}
-
-void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) {
+void TouchState::addGestureMonitors(const std::vector<Monitor>& newMonitors) {
     const size_t newSize = gestureMonitors.size() + newMonitors.size();
     gestureMonitors.reserve(newSize);
     gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors),
@@ -117,9 +105,9 @@
     }
 }
 
-void TouchState::filterNonMonitors() {
-    windows.clear();
-    portalWindows.clear();
+void TouchState::filterWindowsExcept(const sp<IBinder>& token) {
+    std::erase_if(windows,
+                  [&token](const TouchedWindow& w) { return w.windowHandle->getToken() != token; });
 }
 
 sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
@@ -147,4 +135,24 @@
     return haveSlipperyForegroundWindow;
 }
 
+sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
+    for (size_t i = 0; i < windows.size(); i++) {
+        const TouchedWindow& window = windows[i];
+        if (window.windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {
+            return window.windowHandle;
+        }
+    }
+    return nullptr;
+}
+
+sp<WindowInfoHandle> TouchState::getWindow(const sp<IBinder>& token) const {
+    for (const TouchedWindow& touchedWindow : windows) {
+        const auto& windowHandle = touchedWindow.windowHandle;
+        if (windowHandle->getToken() == token) {
+            return windowHandle;
+        }
+    }
+    return nullptr;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index a4e52b0..83ca901 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -36,12 +36,7 @@
     int32_t displayId; // id to the display that currently has a touch, others are rejected
     std::vector<TouchedWindow> windows;
 
-    // This collects the portal windows that the touch has gone through. Each portal window
-    // targets a display (embedded display for most cases). With this info, we can add the
-    // monitoring channels of the displays touched.
-    std::vector<sp<android::gui::WindowInfoHandle>> portalWindows;
-
-    std::vector<TouchedMonitor> gestureMonitors;
+    std::vector<Monitor> gestureMonitors;
 
     TouchState();
     ~TouchState();
@@ -49,13 +44,14 @@
     void copyFrom(const TouchState& other);
     void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
                            int32_t targetFlags, BitSet32 pointerIds);
-    void addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle);
-    void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
+    void addGestureMonitors(const std::vector<Monitor>& monitors);
     void removeWindowByToken(const sp<IBinder>& token);
     void filterNonAsIsTouchWindows();
-    void filterNonMonitors();
+    void filterWindowsExcept(const sp<IBinder>& token);
     sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
     bool isSlippery() const;
+    sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
+    sp<android::gui::WindowInfoHandle> getWindow(const sp<IBinder>&) const;
 };
 
 } // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
index a359557..38d0c32 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -17,7 +17,7 @@
 #ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
 #define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
 
-#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
 
 #include "InputDispatcherInterface.h"
 #include "InputDispatcherPolicyInterface.h"
@@ -25,7 +25,7 @@
 namespace android {
 
 // This factory method is used to encapsulate implementation details in internal header files.
-sp<InputDispatcherInterface> createInputDispatcher(
+std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
         const sp<InputDispatcherPolicyInterface>& policy);
 
 } // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index a7dccf0..c469ec3 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -33,12 +33,10 @@
 
 /* Notifies the system about input events generated by the input reader.
  * The dispatcher is expected to be mostly asynchronous. */
-class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
-protected:
+class InputDispatcherInterface : public InputListenerInterface {
+public:
     InputDispatcherInterface() {}
     virtual ~InputDispatcherInterface() {}
-
-public:
     /* Dumps the state of the input dispatcher.
      *
      * This method may be called on any thread (usually by the input manager). */
@@ -202,6 +200,7 @@
      * InputDispatcher is the source of truth of Pointer Capture.
      */
     virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0;
+
     /* Flush input device motion sensor.
      *
      * Returns true on success.
@@ -212,6 +211,11 @@
      * Called when a display has been removed from the system.
      */
     virtual void displayRemoved(int32_t displayId) = 0;
+
+    /*
+     * Abort the current touch stream.
+     */
+    virtual void cancelCurrentTouch() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index fe74214..db63104 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -22,7 +22,6 @@
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/TouchVideoFrame.h>
-#include <utils/RefBase.h>
 
 namespace android {
 
@@ -40,7 +39,7 @@
 
     virtual ~NotifyArgs() { }
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
+    virtual void notify(InputListenerInterface& listener) const = 0;
 };
 
 
@@ -57,7 +56,7 @@
 
     virtual ~NotifyConfigurationChangedArgs() { }
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 
@@ -88,7 +87,7 @@
 
     virtual ~NotifyKeyArgs() { }
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 
@@ -142,7 +141,7 @@
 
     bool operator==(const NotifyMotionArgs& rhs) const;
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 /* Describes a sensor event. */
@@ -167,7 +166,7 @@
 
     ~NotifySensorArgs() override {}
 
-    void notify(const sp<InputListenerInterface>& listener) const override;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 /* Describes a switch event. */
@@ -187,7 +186,7 @@
 
     virtual ~NotifySwitchArgs() { }
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 
@@ -206,7 +205,7 @@
 
     virtual ~NotifyDeviceResetArgs() { }
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 /* Describes a change in the state of Pointer Capture. */
@@ -224,7 +223,7 @@
 
     virtual ~NotifyPointerCaptureChangedArgs() {}
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 /* Describes a vibrator state event. */
@@ -242,18 +241,19 @@
 
     virtual ~NotifyVibratorStateArgs() {}
 
-    virtual void notify(const sp<InputListenerInterface>& listener) const;
+    void notify(InputListenerInterface& listener) const override;
 };
 
 /*
  * The interface used by the InputReader to notify the InputListener about input events.
  */
-class InputListenerInterface : public virtual RefBase {
-protected:
+class InputListenerInterface {
+public:
     InputListenerInterface() { }
+    InputListenerInterface(const InputListenerInterface&) = delete;
+    InputListenerInterface& operator=(const InputListenerInterface&) = delete;
     virtual ~InputListenerInterface() { }
 
-public:
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0;
     virtual void notifyKey(const NotifyKeyArgs* args) = 0;
     virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
@@ -264,17 +264,15 @@
     virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
 };
 
-
 /*
  * An implementation of the listener interface that queues up and defers dispatch
  * of decoded events until flushed.
  */
 class QueuedInputListener : public InputListenerInterface {
-protected:
-    virtual ~QueuedInputListener();
 
 public:
-    explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);
+    explicit QueuedInputListener(InputListenerInterface& innerListener);
+    virtual ~QueuedInputListener();
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
     virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -288,7 +286,7 @@
     void flush();
 
 private:
-    sp<InputListenerInterface> mInnerListener;
+    InputListenerInterface& mInnerListener;
     std::vector<NotifyArgs*> mArgsQueue;
 };
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 3c8ac1c..1aab856 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -51,12 +51,11 @@
  * The implementation must guarantee thread safety for this interface. However, since the input
  * listener is NOT thread safe, all calls to the listener must happen from the same thread.
  */
-class InputReaderInterface : public virtual RefBase {
-protected:
+class InputReaderInterface {
+public:
     InputReaderInterface() { }
     virtual ~InputReaderInterface() { }
 
-public:
     /* Dumps the state of the input reader.
      *
      * This method may be called on any thread (usually by the input manager). */
diff --git a/services/inputflinger/include/InputReaderFactory.h b/services/inputflinger/include/InputReaderFactory.h
index 9db6233..dad14d6 100644
--- a/services/inputflinger/include/InputReaderFactory.h
+++ b/services/inputflinger/include/InputReaderFactory.h
@@ -22,8 +22,7 @@
 class InputReaderPolicyInterface;
 class InputListenerInterface;
 
-sp<InputReaderInterface> createInputReader(
-        const sp<InputReaderPolicyInterface>& policy,
-        const sp<InputListenerInterface>& listener);
+std::unique_ptr<InputReaderInterface> createInputReader(
+        const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener);
 
 } // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 85d7247..db4228d 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -20,7 +20,6 @@
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <utils/BitSet.h>
-#include <utils/RefBase.h>
 
 namespace android {
 
@@ -31,7 +30,8 @@
  * fingers
  *
  * The pointer controller is responsible for providing synchronization and for tracking
- * display orientation changes if needed.
+ * display orientation changes if needed. It works in the display panel's coordinate space, which
+ * is the same coordinate space used by InputReader.
  */
 class PointerControllerInterface {
 protected:
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index ee7b392..51546ce 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,7 +71,6 @@
         "libstatslog",
         "libui",
         "libutils",
-        "InputFlingerProperties",
     ],
     static_libs: [
         "libc++fs",
@@ -86,7 +85,7 @@
     name: "libinputreader",
     defaults: [
         "inputflinger_defaults",
-        "libinputreader_defaults"
+        "libinputreader_defaults",
     ],
     srcs: [
         "InputReaderFactory.cpp",
@@ -100,6 +99,6 @@
         "libinputreader_headers",
     ],
     static_libs: [
-        "libc++fs"
+        "libc++fs",
     ],
 }
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 166f358..09a62f3 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -40,6 +40,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/properties.h>
+#include <ftl/enum.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
 #include <input/VirtualKeyMap.h>
@@ -263,7 +264,7 @@
  */
 static std::vector<std::filesystem::path> findSysfsNodes(const std::filesystem::path& sysfsRoot,
                                                          SysfsClass clazz) {
-    std::string nodeStr = NamedEnum::string(clazz);
+    std::string nodeStr = ftl::enum_string(clazz);
     std::for_each(nodeStr.begin(), nodeStr.end(),
                   [](char& c) { c = std::tolower(static_cast<unsigned char>(c)); });
     std::vector<std::filesystem::path> nodes;
@@ -1209,6 +1210,15 @@
     return false;
 }
 
+bool EventHub::hasKeyCode(int32_t deviceId, int32_t keyCode) const {
+    std::scoped_lock _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+    if (device != nullptr) {
+        return device->hasKeycodeLocked(keyCode);
+    }
+    return false;
+}
+
 bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
     std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
@@ -2333,13 +2343,10 @@
             return;
         }
     }
-    mUnattachedVideoDevices
-            .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
-                                  [&devicePath](
-                                          const std::unique_ptr<TouchVideoDevice>& videoDevice) {
-                                      return videoDevice->getPath() == devicePath;
-                                  }),
-                   mUnattachedVideoDevices.end());
+    std::erase_if(mUnattachedVideoDevices,
+                  [&devicePath](const std::unique_ptr<TouchVideoDevice>& videoDevice) {
+                      return videoDevice->getPath() == devicePath;
+                  });
 }
 
 void EventHub::closeAllDevicesLocked() {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 1e9ec54..7cff672 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -379,21 +379,22 @@
     // gamepad button presses are handled by different mappers but they should be dispatched
     // in the order received.
     for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
-#if DEBUG_RAW_EVENTS
-        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
-              rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when);
-#endif
+        if (DEBUG_RAW_EVENTS) {
+            ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
+                  rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
+                  rawEvent->when);
+        }
 
         if (mDropUntilNextSync) {
             if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                 mDropUntilNextSync = false;
-#if DEBUG_RAW_EVENTS
-                ALOGD("Recovered from input event buffer overrun.");
-#endif
+                if (DEBUG_RAW_EVENTS) {
+                    ALOGD("Recovered from input event buffer overrun.");
+                }
             } else {
-#if DEBUG_RAW_EVENTS
-                ALOGD("Dropped input event while waiting for next input sync.");
-#endif
+                if (DEBUG_RAW_EVENTS) {
+                    ALOGD("Dropped input event while waiting for next input sync.");
+                }
             }
         } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
             ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
@@ -559,7 +560,13 @@
 }
 
 void InputDevice::updateMetaState(int32_t keyCode) {
-    for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); });
+    first_in_mappers<bool>([keyCode](InputMapper& mapper) {
+        if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD) &&
+            mapper.updateMetaState(keyCode)) {
+            return std::make_optional(true);
+        }
+        return std::optional<bool>();
+    });
 }
 
 void InputDevice::bumpGeneration() {
@@ -568,7 +575,7 @@
 
 void InputDevice::notifyReset(nsecs_t when) {
     NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
-    mContext->getListener()->notifyDeviceReset(&args);
+    mContext->getListener().notifyDeviceReset(&args);
 }
 
 std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 5120860..564db56 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -42,10 +42,11 @@
 
 InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                          const sp<InputReaderPolicyInterface>& policy,
-                         const sp<InputListenerInterface>& listener)
+                         InputListenerInterface& listener)
       : mContext(this),
         mEventHub(eventHub),
         mPolicy(policy),
+        mQueuedListener(listener),
         mGlobalMetaState(0),
         mLedMetaState(AMETA_NUM_LOCK_ON),
         mGeneration(1),
@@ -53,14 +54,8 @@
         mDisableVirtualKeysTimeout(LLONG_MIN),
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
-    mQueuedListener = new QueuedInputListener(listener);
-
-    { // acquire lock
-        std::scoped_lock _l(mLock);
-
-        refreshConfigurationLocked(0);
-        updateGlobalMetaStateLocked();
-    } // release lock
+    refreshConfigurationLocked(0);
+    updateGlobalMetaStateLocked();
 }
 
 InputReader::~InputReader() {}
@@ -118,9 +113,9 @@
         if (mNextTimeout != LLONG_MAX) {
             nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
             if (now >= mNextTimeout) {
-#if DEBUG_RAW_EVENTS
-                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
-#endif
+                if (DEBUG_RAW_EVENTS) {
+                    ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
+                }
                 mNextTimeout = LLONG_MAX;
                 timeoutExpiredLocked(now);
             }
@@ -144,7 +139,7 @@
     // resulting in a deadlock.  This situation is actually quite plausible because the
     // listener is actually the input dispatcher, which calls into the window manager,
     // which occasionally calls into the input reader.
-    mQueuedListener->flush();
+    mQueuedListener.flush();
 }
 
 void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
@@ -160,9 +155,9 @@
                 }
                 batchSize += 1;
             }
-#if DEBUG_RAW_EVENTS
-            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
-#endif
+            if (DEBUG_RAW_EVENTS) {
+                ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
+            }
             processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
         } else {
             switch (rawEvent->type) {
@@ -242,9 +237,7 @@
     auto mapIt = mDeviceToEventHubIdsMap.find(device);
     if (mapIt != mDeviceToEventHubIdsMap.end()) {
         std::vector<int32_t>& eventHubIds = mapIt->second;
-        eventHubIds.erase(std::remove_if(eventHubIds.begin(), eventHubIds.end(),
-                                         [eventHubId](int32_t eId) { return eId == eventHubId; }),
-                          eventHubIds.end());
+        std::erase_if(eventHubIds, [eventHubId](int32_t eId) { return eId == eventHubId; });
         if (eventHubIds.size() == 0) {
             mDeviceToEventHubIdsMap.erase(mapIt);
         }
@@ -340,7 +333,7 @@
 
     // Enqueue configuration changed.
     NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
-    mQueuedListener->notifyConfigurationChanged(&args);
+    mQueuedListener.notifyConfigurationChanged(&args);
 }
 
 void InputReader::refreshConfigurationLocked(uint32_t changes) {
@@ -374,7 +367,7 @@
             mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
             const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
                                                        mCurrentPointerCaptureRequest);
-            mQueuedListener->notifyPointerCaptureChanged(&args);
+            mQueuedListener.notifyPointerCaptureChanged(&args);
         }
     }
 }
@@ -561,6 +554,7 @@
     }
 
     if (device->isIgnored()) {
+        ALOGW("Ignoring toggleCapsLock for ignored deviceId %" PRId32 ".", deviceId);
         return;
     }
 
@@ -786,12 +780,8 @@
 
     std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
     // No associated display. By default, can dispatch to all displays.
-    if (!associatedDisplayId) {
-        return true;
-    }
-
-    if (*associatedDisplayId == ADISPLAY_ID_NONE) {
-        ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
+    if (!associatedDisplayId ||
+            *associatedDisplayId == ADISPLAY_ID_NONE) {
         return true;
     }
 
@@ -951,8 +941,8 @@
     return mReader->mPolicy.get();
 }
 
-InputListenerInterface* InputReader::ContextImpl::getListener() {
-    return mReader->mQueuedListener.get();
+InputListenerInterface& InputReader::ContextImpl::getListener() {
+    return mReader->mQueuedListener;
 }
 
 EventHubInterface* InputReader::ContextImpl::getEventHub() {
diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp
index a897141..2d9ffc3 100644
--- a/services/inputflinger/reader/InputReaderFactory.cpp
+++ b/services/inputflinger/reader/InputReaderFactory.cpp
@@ -20,9 +20,9 @@
 
 namespace android {
 
-sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
-                                           const sp<InputListenerInterface>& listener) {
-    return new InputReader(std::make_unique<EventHub>(), policy, listener);
+std::unique_ptr<InputReaderInterface> createInputReader(
+        const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) {
+    return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);
 }
 
 } // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index 827e31a..d837689 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -22,28 +22,25 @@
 //#define LOG_NDEBUG 0
 
 // Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
+static constexpr bool DEBUG_RAW_EVENTS = false;
 
 // Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
+static constexpr bool DEBUG_VIRTUAL_KEYS = false;
 
 // Log debug messages about pointers.
-#define DEBUG_POINTERS 0
+static constexpr bool DEBUG_POINTERS = false;
 
 // Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
+static constexpr bool DEBUG_POINTER_ASSIGNMENT = false;
 
 // Log debug messages about gesture detection.
-#define DEBUG_GESTURES 0
+static constexpr bool DEBUG_GESTURES = false;
 
 // Log debug messages about the vibrator.
-#define DEBUG_VIBRATOR 0
+static constexpr bool DEBUG_VIBRATOR = false;
 
 // Log debug messages about fusing stylus data.
-#define DEBUG_STYLUS_FUSION 0
+static constexpr bool DEBUG_STYLUS_FUSION = false;
 
 #define INDENT "  "
 #define INDENT2 "    "
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 9c8a29a..a693496 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -17,9 +17,9 @@
 #include <locale>
 #include <regex>
 
-#include "../Macros.h"
+#include <ftl/enum.h>
 
-#include <ftl/NamedEnum.h>
+#include "../Macros.h"
 #include "PeripheralController.h"
 
 // Log detailed debug messages about input device lights.
@@ -286,7 +286,7 @@
         for (const auto& [lightId, light] : mLights) {
             dump += StringPrintf(INDENT4 "Id: %d", lightId);
             dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
-            dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
+            dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str());
             light->dump(dump);
         }
     }
@@ -487,7 +487,7 @@
     auto& light = it->second;
     if (DEBUG_LIGHT_DETAILS) {
         ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
-              NamedEnum::string(light->type).c_str(), color);
+              ftl::enum_string(light->type).c_str(), color);
     }
     return light->setLightColor(color);
 }
@@ -501,7 +501,7 @@
     std::optional<int32_t> color = light->getLightColor();
     if (DEBUG_LIGHT_DETAILS) {
         ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
-              NamedEnum::string(light->type).c_str(), color.value_or(0));
+              ftl::enum_string(light->type).c_str(), color.value_or(0));
     }
     return color;
 }
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 3c3f88e..1f96294 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -146,6 +146,8 @@
 enum class SysfsClass : uint32_t {
     POWER_SUPPLY = 0,
     LEDS = 1,
+
+    ftl_last = LEDS
 };
 
 enum class LightColor : uint32_t {
@@ -312,6 +314,7 @@
                                        uint8_t* outFlags) const = 0;
 
     virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0;
+    virtual bool hasKeyCode(int32_t deviceId, int32_t keyCode) const = 0;
 
     /* LED related functions expect Android LED constants, not scan codes or HID usages */
     virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
@@ -489,6 +492,7 @@
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
 
     bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
+    bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override final;
     bool hasLed(int32_t deviceId, int32_t led) const override final;
     void setLedState(int32_t deviceId, int32_t led, bool on) override final;
 
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index f32472d..518aaa0 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -323,6 +323,7 @@
     inline bool hasScanCode(int32_t scanCode) const {
         return mEventHub->hasScanCode(mId, scanCode);
     }
+    inline bool hasKeyCode(int32_t keyCode) const { return mEventHub->hasKeyCode(mId, keyCode); }
     inline bool hasLed(int32_t led) const { return mEventHub->hasLed(mId, led); }
     inline void setLedState(int32_t led, bool on) { return mEventHub->setLedState(mId, led, on); }
     inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index e44aa0f..fb1d166 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -52,8 +52,7 @@
 class InputReader : public InputReaderInterface {
 public:
     InputReader(std::shared_ptr<EventHubInterface> eventHub,
-                const sp<InputReaderPolicyInterface>& policy,
-                const sp<InputListenerInterface>& listener);
+                const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener);
     virtual ~InputReader();
 
     void dump(std::string& dump) override;
@@ -143,7 +142,7 @@
         void dispatchExternalStylusState(const StylusState& outState)
                 REQUIRES(mReader->mLock) override;
         InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override;
-        InputListenerInterface* getListener() REQUIRES(mReader->mLock) override;
+        InputListenerInterface& getListener() REQUIRES(mReader->mLock) override;
         EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override;
         int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
         void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
@@ -164,7 +163,7 @@
     // in parallel to passing it to the InputReader.
     std::shared_ptr<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
-    sp<QueuedInputListener> mQueuedListener;
+    QueuedInputListener mQueuedListener;
 
     InputReaderConfiguration mConfig GUARDED_BY(mLock);
 
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index dc807f7..823d160 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,7 +55,7 @@
     virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
-    virtual InputListenerInterface* getListener() = 0;
+    virtual InputListenerInterface& getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 
     virtual int32_t getNextId() = 0;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 2ac41b1..fcb56ef 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -176,7 +176,7 @@
         bumpGeneration();
         if (changes) {
             NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
-            getListener()->notifyDeviceReset(&args);
+            getListener().notifyDeviceReset(&args);
         }
     }
 
@@ -188,33 +188,19 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         mOrientation = DISPLAY_ORIENTATION_0;
-        mDisplayWidth = 0;
-        mDisplayHeight = 0;
         const bool isOrientedDevice =
                 (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
 
-        if (isPerWindowInputRotationEnabled()) {
-            // When per-window input rotation is enabled, InputReader works in the un-rotated
-            // coordinate space, so we don't need to do anything if the device is already
-            // orientation-aware. If the device is not orientation-aware, then we need to apply the
-            // inverse rotation of the display so that when the display rotation is applied later
-            // as a part of the per-window transform, we get the expected screen coordinates.
-            if (!isOrientedDevice) {
-                std::optional<DisplayViewport> internalViewport =
-                        config->getDisplayViewportByType(ViewportType::INTERNAL);
-                if (internalViewport) {
-                    mOrientation = getInverseRotation(internalViewport->orientation);
-                    mDisplayWidth = internalViewport->deviceWidth;
-                    mDisplayHeight = internalViewport->deviceHeight;
-                }
-            }
-        } else {
-            if (isOrientedDevice) {
-                std::optional<DisplayViewport> internalViewport =
-                        config->getDisplayViewportByType(ViewportType::INTERNAL);
-                if (internalViewport) {
-                    mOrientation = internalViewport->orientation;
-                }
+        // InputReader works in the un-rotated display coordinate space, so we don't need to do
+        // anything if the device is already orientation-aware. If the device is not
+        // orientation-aware, then we need to apply the inverse rotation of the display so that
+        // when the display rotation is applied later as a part of the per-window transform, we
+        // get the expected screen coordinates.
+        if (!isOrientedDevice) {
+            std::optional<DisplayViewport> internalViewport =
+                    config->getDisplayViewportByType(ViewportType::INTERNAL);
+            if (internalViewport) {
+                mOrientation = getInverseRotation(internalViewport->orientation);
             }
         }
 
@@ -345,15 +331,7 @@
             mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
 
             if (moved) {
-                float dx = deltaX;
-                float dy = deltaY;
-                if (isPerWindowInputRotationEnabled()) {
-                    // Rotate the delta from InputReader's un-rotated coordinate space to
-                    // PointerController's rotated coordinate space that is oriented with the
-                    // viewport.
-                    rotateDelta(getInverseRotation(mOrientation), &dx, &dy);
-                }
-                mPointerController->move(dx, dy);
+                mPointerController->move(deltaX, deltaY);
             }
 
             if (buttonsChanged) {
@@ -364,12 +342,7 @@
         }
 
         mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
-        if (isPerWindowInputRotationEnabled()) {
-            // Rotate the cursor position that is in PointerController's rotated coordinate space
-            // to InputReader's un-rotated coordinate space.
-            rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/,
-                        mDisplayWidth, mDisplayHeight);
-        }
+
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
@@ -424,7 +397,7 @@
                                              &pointerCoords, mXPrecision, mYPrecision,
                                              xCursorPosition, yCursorPosition, downTime,
                                              /* videoFrames */ {});
-                getListener()->notifyMotion(&releaseArgs);
+                getListener().notifyMotion(&releaseArgs);
             }
         }
 
@@ -434,7 +407,7 @@
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
                               mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
                               /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getListener().notifyMotion(&args);
 
         if (buttonsPressed) {
             BitSet32 pressed(buttonsPressed);
@@ -449,7 +422,7 @@
                                            &pointerCoords, mXPrecision, mYPrecision,
                                            xCursorPosition, yCursorPosition, downTime,
                                            /* videoFrames */ {});
-                getListener()->notifyMotion(&pressArgs);
+                getListener().notifyMotion(&pressArgs);
             }
         }
 
@@ -464,7 +437,7 @@
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                        yCursorPosition, downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&hoverArgs);
+            getListener().notifyMotion(&hoverArgs);
         }
 
         // Send scroll events.
@@ -479,7 +452,7 @@
                                         AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                         &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                         yCursorPosition, downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&scrollArgs);
+            getListener().notifyMotion(&scrollArgs);
         }
     }
 
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 88e947f..9a8ca01 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -105,8 +105,6 @@
     VelocityControl mWheelYVelocityControl;
 
     int32_t mOrientation;
-    int32_t mDisplayWidth;
-    int32_t mDisplayHeight;
 
     std::shared_ptr<PointerControllerInterface> mPointerController;
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index df1acd4..b9aef54 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -84,7 +84,9 @@
     return 0;
 }
 
-void InputMapper::updateMetaState(int32_t keyCode) {}
+bool InputMapper::updateMetaState(int32_t keyCode) {
+    return false;
+}
 
 void InputMapper::updateExternalStylusState(const StylusState& state) {}
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 15cff1c..d83d74d 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -45,10 +45,10 @@
 
     inline int32_t getDeviceId() { return mDeviceContext.getId(); }
     inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
-    inline const std::string getDeviceName() { return mDeviceContext.getName(); }
+    inline const std::string getDeviceName() const { return mDeviceContext.getName(); }
     inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
     inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
-    inline InputListenerInterface* getListener() { return getContext()->getListener(); }
+    inline InputListenerInterface& getListener() { return getContext()->getListener(); }
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
@@ -83,7 +83,11 @@
     virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; }
 
     virtual int32_t getMetaState();
-    virtual void updateMetaState(int32_t keyCode);
+    /**
+     * Process the meta key and update the global meta state when changed.
+     * Return true if the meta key could be handled by the InputMapper.
+     */
+    virtual bool updateMetaState(int32_t keyCode);
 
     virtual void updateExternalStylusState(const StylusState& state);
 
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 0dc312e..ad11b05 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -32,8 +32,7 @@
 void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    for (std::pair<const int32_t, Axis>& pair : mAxes) {
-        const Axis& axis = pair.second;
+    for (const auto& [_, axis] : mAxes) {
         addMotionRange(axis.axisInfo.axis, axis, info);
 
         if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
@@ -344,7 +343,7 @@
                           &pointerProperties, &pointerCoords, 0, 0,
                           AMOTION_EVENT_INVALID_CURSOR_POSITION,
                           AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-    getListener()->notifyMotion(&args);
+    getListener().notifyMotion(&args);
 }
 
 void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 104d087..a8602a4 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -354,7 +354,7 @@
                        getDisplayId(), policyFlags,
                        down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                        AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
-    getListener()->notifyKey(&args);
+    getListener().notifyKey(&args);
 }
 
 ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
@@ -384,8 +384,13 @@
     return mMetaState;
 }
 
-void KeyboardInputMapper::updateMetaState(int32_t keyCode) {
+bool KeyboardInputMapper::updateMetaState(int32_t keyCode) {
+    if (!android::isMetaKey(keyCode) || !getDeviceContext().hasKeyCode(keyCode)) {
+        return false;
+    }
+
     updateMetaStateIfNeeded(keyCode, false);
+    return true;
 }
 
 bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index ca41712..fc92320 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -40,7 +40,7 @@
                                        const int32_t* keyCodes, uint8_t* outFlags) override;
 
     virtual int32_t getMetaState() override;
-    virtual void updateMetaState(int32_t keyCode) override;
+    virtual bool updateMetaState(int32_t keyCode) override;
     virtual std::optional<int32_t> getAssociatedDisplayId() override;
     virtual void updateLedState(bool reset);
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index fab7f4c..4bd1cd8 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -95,13 +95,13 @@
         }
 
         if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
-#if DEBUG_POINTERS
-            if (newSlot) {
-                ALOGW("MultiTouch device emitted invalid slot index %d but it "
-                      "should be between 0 and %zd; ignoring this slot.",
-                      mCurrentSlot, mSlotCount - 1);
+            if (DEBUG_POINTERS) {
+                if (newSlot) {
+                    ALOGW("MultiTouch device emitted invalid slot index %d but it "
+                          "should be between 0 and %zd; ignoring this slot.",
+                          mCurrentSlot, mSlotCount - 1);
+                }
             }
-#endif
         } else {
             Slot* slot = &mSlots[mCurrentSlot];
             // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
@@ -273,19 +273,19 @@
             if (id) {
                 outState->rawPointerData.canceledIdBits.markBit(id.value());
             }
-#if DEBUG_POINTERS
-            ALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex,
-                  getDeviceName().c_str());
-#endif
+            if (DEBUG_POINTERS) {
+                ALOGI("Stop processing slot %zu for it received a palm event from device %s",
+                      inIndex, getDeviceName().c_str());
+            }
             continue;
         }
 
         if (outCount >= MAX_POINTERS) {
-#if DEBUG_POINTERS
-            ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
-                  "ignoring the rest.",
-                  getDeviceName().c_str(), MAX_POINTERS);
-#endif
+            if (DEBUG_POINTERS) {
+                ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
+                      "ignoring the rest.",
+                      getDeviceName().c_str(), MAX_POINTERS);
+            }
             break; // too many fingers!
         }
 
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index e9d0189..b83a8fc 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -127,7 +127,7 @@
                                     AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                     &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                     AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-        getListener()->notifyMotion(&scrollArgs);
+        getListener().notifyMotion(&scrollArgs);
     }
 
     mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index a507632..677a372 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -16,8 +16,9 @@
 
 #include <locale>
 
-#include "../Macros.h"
+#include <ftl/enum.h>
 
+#include "../Macros.h"
 #include "SensorInputMapper.h"
 
 // Log detailed debug messages about each sensor event notification to the dispatcher.
@@ -93,7 +94,7 @@
     dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp);
     dump += INDENT3 "Sensors:\n";
     for (const auto& [sensorType, sensor] : mSensors) {
-        dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str());
+        dump += StringPrintf(INDENT4 "%s\n", ftl::enum_string(sensorType).c_str());
         dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled);
         dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count());
         dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n",
@@ -208,10 +209,10 @@
                                      axis.max /* maxRange */, axis.scale /* resolution */,
                                      0.0f /* power */, 0 /* minDelay */,
                                      0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */,
-                                     NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */,
+                                     ftl::enum_string(sensorType), 0 /* maxDelay */, 0 /* flags */,
                                      getDeviceId());
 
-    std::string prefix = "sensor." + NamedEnum::string(sensorType);
+    std::string prefix = "sensor." + ftl::enum_string(sensorType);
     transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
 
     int32_t reportingMode = 0;
@@ -335,7 +336,7 @@
                                      std::chrono::microseconds maxBatchReportLatency) {
     if (DEBUG_SENSOR_EVENT_DETAILS) {
         ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
-              NamedEnum::string(sensorType).c_str(), samplingPeriod.count(),
+              ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
               maxBatchReportLatency.count());
     }
 
@@ -359,7 +360,7 @@
 
 void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
     if (DEBUG_SENSOR_EVENT_DETAILS) {
-        ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str());
+        ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str());
     }
 
     if (!setSensorEnabled(sensorType, false /* enabled */)) {
@@ -393,13 +394,12 @@
         nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
         if (DEBUG_SENSOR_EVENT_DETAILS) {
             ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
-                  NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1],
-                  values[2]);
+                  ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
         }
         if (sensor.lastSampleTimeNs.has_value() &&
             timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
             if (DEBUG_SENSOR_EVENT_DETAILS) {
-                ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str());
+                ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str());
             }
         } else {
             // Convert to Android unit
@@ -411,7 +411,7 @@
                                           sensor.sensorInfo.accuracy /* accuracyChanged */,
                                   timestamp /* hwTimestamp */, values);
 
-            getListener()->notifySensor(&args);
+            getListener().notifySensor(&args);
             sensor.lastSampleTimeNs = timestamp;
             sensor.accuracy = sensor.sensorInfo.accuracy;
         }
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4f73681..3237824 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -58,7 +58,7 @@
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
         NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
                               updatedSwitchValues, mUpdatedSwitchMask);
-        getListener()->notifySwitch(&args);
+        getListener().notifySwitch(&args);
 
         mUpdatedSwitchMask = 0;
     }
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 7347b2c..31a3d2e 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,7 +17,6 @@
 #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 
-#include <InputFlingerProperties.sysprop.h>
 #include <input/DisplayViewport.h>
 #include <stdint.h>
 
@@ -29,13 +28,6 @@
 
 // --- Static Definitions ---
 
-// When per-window input rotation is enabled, display transformations such as rotation and
-// projection are part of the input window's transform. This means InputReader should work in the
-// un-rotated coordinate space.
-static bool isPerWindowInputRotationEnabled() {
-    return sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
-}
-
 static int32_t getInverseRotation(int32_t orientation) {
     switch (orientation) {
         case DISPLAY_ORIENTATION_90:
@@ -72,26 +64,6 @@
     }
 }
 
-// Rotates the given point (x, y) by the supplied orientation. The width and height are the
-// dimensions of the surface prior to this rotation being applied.
-static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) {
-    rotateDelta(orientation, &x, &y);
-    switch (orientation) {
-        case DISPLAY_ORIENTATION_90:
-            y += width;
-            break;
-        case DISPLAY_ORIENTATION_180:
-            x += width;
-            y += height;
-            break;
-        case DISPLAY_ORIENTATION_270:
-            x += height;
-            break;
-        default:
-            break;
-    }
-}
-
 // Returns true if the pointer should be reported as being down given the specified
 // button states.  This determines whether the event is reported as a touch event.
 static bool isPointerDown(int32_t buttonState) {
@@ -110,7 +82,7 @@
          !(currentButtonState & buttonState))) {
         NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
                            policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
-        context->getListener()->notifyKey(&args);
+        context->getListener().notifyKey(&args);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ac5f6b6..0a5de27 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -18,9 +18,10 @@
 #include "../Macros.h"
 // clang-format on
 
-#include <ftl/NamedEnum.h>
 #include "TouchInputMapper.h"
 
+#include <ftl/enum.h>
+
 #include "CursorButtonAccumulator.h"
 #include "CursorScrollAccumulator.h"
 #include "TouchButtonAccumulator.h"
@@ -167,17 +168,13 @@
       : InputMapper(deviceContext),
         mSource(0),
         mDeviceMode(DeviceMode::DISABLED),
-        mRawSurfaceWidth(-1),
-        mRawSurfaceHeight(-1),
-        mSurfaceLeft(0),
-        mSurfaceTop(0),
-        mSurfaceRight(0),
-        mSurfaceBottom(0),
+        mDisplayWidth(-1),
+        mDisplayHeight(-1),
         mPhysicalWidth(-1),
         mPhysicalHeight(-1),
         mPhysicalLeft(0),
         mPhysicalTop(0),
-        mSurfaceOrientation(DISPLAY_ORIENTATION_0) {}
+        mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {}
 
 TouchInputMapper::~TouchInputMapper() {}
 
@@ -259,17 +256,15 @@
 
 void TouchInputMapper::dump(std::string& dump) {
     dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n",
-                         NamedEnum::string(mDeviceMode).c_str());
+                         ftl::enum_string(mDeviceMode).c_str());
     dumpParameters(dump);
     dumpVirtualKeys(dump);
     dumpRawPointerAxes(dump);
     dumpCalibration(dump);
     dumpAffineTransformation(dump);
-    dumpSurface(dump);
+    dumpDisplay(dump);
 
     dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
-    dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
-    dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
     dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
     dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
     dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
@@ -389,16 +384,16 @@
           InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
           InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
           InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
-        // Configure device sources, surface dimensions, orientation and
+        // Configure device sources, display dimensions, orientation and
         // scaling factors.
-        configureSurface(when, &resetNeeded);
+        configureInputDevice(when, &resetNeeded);
     }
 
     if (changes && resetNeeded) {
         // Send reset, unless this is the first time the device has been configured,
         // in which case the reader will call reset itself after all mappers are ready.
         NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
-        getListener()->notifyDeviceReset(&args);
+        getListener().notifyDeviceReset(&args);
     }
 }
 
@@ -515,9 +510,9 @@
 void TouchInputMapper::dumpParameters(std::string& dump) {
     dump += INDENT3 "Parameters:\n";
 
-    dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n";
+    dump += INDENT4 "GestureMode: " + ftl::enum_string(mParameters.gestureMode) + "\n";
 
-    dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n";
+    dump += INDENT4 "DeviceType: " + ftl::enum_string(mParameters.deviceType) + "\n";
 
     dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
                                  "displayId='%s'\n",
@@ -525,7 +520,7 @@
                          toString(mParameters.associatedDisplayIsExternal),
                          mParameters.uniqueDisplayId.c_str());
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
-    dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n";
+    dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n";
 }
 
 void TouchInputMapper::configureRawPointerAxes() {
@@ -614,7 +609,271 @@
     return std::make_optional(newViewport);
 }
 
-void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
+int32_t TouchInputMapper::clampResolution(const char* axisName, int32_t resolution) const {
+    if (resolution < 0) {
+        ALOGE("Invalid %s resolution %" PRId32 " for device %s", axisName, resolution,
+              getDeviceName().c_str());
+        return 0;
+    }
+    return resolution;
+}
+
+void TouchInputMapper::initializeSizeRanges() {
+    if (mCalibration.sizeCalibration == Calibration::SizeCalibration::NONE) {
+        mSizeScale = 0.0f;
+        return;
+    }
+
+    // Size of diagonal axis.
+    const float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight);
+
+    // Size factors.
+    if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
+        mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
+    } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
+        mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
+    } else {
+        mSizeScale = 0.0f;
+    }
+
+    mOrientedRanges.haveTouchSize = true;
+    mOrientedRanges.haveToolSize = true;
+    mOrientedRanges.haveSize = true;
+
+    mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
+    mOrientedRanges.touchMajor.source = mSource;
+    mOrientedRanges.touchMajor.min = 0;
+    mOrientedRanges.touchMajor.max = diagonalSize;
+    mOrientedRanges.touchMajor.flat = 0;
+    mOrientedRanges.touchMajor.fuzz = 0;
+    mOrientedRanges.touchMajor.resolution = 0;
+    if (mRawPointerAxes.touchMajor.valid) {
+        mRawPointerAxes.touchMajor.resolution =
+                clampResolution("touchMajor", mRawPointerAxes.touchMajor.resolution);
+        mOrientedRanges.touchMajor.resolution = mRawPointerAxes.touchMajor.resolution;
+    }
+
+    mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
+    mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
+    if (mRawPointerAxes.touchMinor.valid) {
+        mRawPointerAxes.touchMinor.resolution =
+                clampResolution("touchMinor", mRawPointerAxes.touchMinor.resolution);
+        mOrientedRanges.touchMinor.resolution = mRawPointerAxes.touchMinor.resolution;
+    }
+
+    mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
+    mOrientedRanges.toolMajor.source = mSource;
+    mOrientedRanges.toolMajor.min = 0;
+    mOrientedRanges.toolMajor.max = diagonalSize;
+    mOrientedRanges.toolMajor.flat = 0;
+    mOrientedRanges.toolMajor.fuzz = 0;
+    mOrientedRanges.toolMajor.resolution = 0;
+    if (mRawPointerAxes.toolMajor.valid) {
+        mRawPointerAxes.toolMajor.resolution =
+                clampResolution("toolMajor", mRawPointerAxes.toolMajor.resolution);
+        mOrientedRanges.toolMajor.resolution = mRawPointerAxes.toolMajor.resolution;
+    }
+
+    mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
+    mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
+    if (mRawPointerAxes.toolMinor.valid) {
+        mRawPointerAxes.toolMinor.resolution =
+                clampResolution("toolMinor", mRawPointerAxes.toolMinor.resolution);
+        mOrientedRanges.toolMinor.resolution = mRawPointerAxes.toolMinor.resolution;
+    }
+
+    if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
+        mOrientedRanges.touchMajor.resolution *= mGeometricScale;
+        mOrientedRanges.touchMinor.resolution *= mGeometricScale;
+        mOrientedRanges.toolMajor.resolution *= mGeometricScale;
+        mOrientedRanges.toolMinor.resolution *= mGeometricScale;
+    } else {
+        // Support for other calibrations can be added here.
+        ALOGW("%s calibration is not supported for size ranges at the moment. "
+              "Using raw resolution instead",
+              ftl::enum_string(mCalibration.sizeCalibration).c_str());
+    }
+
+    mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
+    mOrientedRanges.size.source = mSource;
+    mOrientedRanges.size.min = 0;
+    mOrientedRanges.size.max = 1.0;
+    mOrientedRanges.size.flat = 0;
+    mOrientedRanges.size.fuzz = 0;
+    mOrientedRanges.size.resolution = 0;
+}
+
+void TouchInputMapper::initializeOrientedRanges() {
+    // Configure X and Y factors.
+    mXScale = float(mDisplayWidth) / mRawPointerAxes.getRawWidth();
+    mYScale = float(mDisplayHeight) / mRawPointerAxes.getRawHeight();
+    mXPrecision = 1.0f / mXScale;
+    mYPrecision = 1.0f / mYScale;
+
+    mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
+    mOrientedRanges.x.source = mSource;
+    mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
+    mOrientedRanges.y.source = mSource;
+
+    // Scale factor for terms that are not oriented in a particular axis.
+    // If the pixels are square then xScale == yScale otherwise we fake it
+    // by choosing an average.
+    mGeometricScale = avg(mXScale, mYScale);
+
+    initializeSizeRanges();
+
+    // Pressure factors.
+    mPressureScale = 0;
+    float pressureMax = 1.0;
+    if (mCalibration.pressureCalibration == Calibration::PressureCalibration::PHYSICAL ||
+        mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
+        if (mCalibration.havePressureScale) {
+            mPressureScale = mCalibration.pressureScale;
+            pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
+        } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
+            mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
+        }
+    }
+
+    mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
+    mOrientedRanges.pressure.source = mSource;
+    mOrientedRanges.pressure.min = 0;
+    mOrientedRanges.pressure.max = pressureMax;
+    mOrientedRanges.pressure.flat = 0;
+    mOrientedRanges.pressure.fuzz = 0;
+    mOrientedRanges.pressure.resolution = 0;
+
+    // Tilt
+    mTiltXCenter = 0;
+    mTiltXScale = 0;
+    mTiltYCenter = 0;
+    mTiltYScale = 0;
+    mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
+    if (mHaveTilt) {
+        mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue);
+        mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue);
+        mTiltXScale = M_PI / 180;
+        mTiltYScale = M_PI / 180;
+
+        if (mRawPointerAxes.tiltX.resolution) {
+            mTiltXScale = 1.0 / mRawPointerAxes.tiltX.resolution;
+        }
+        if (mRawPointerAxes.tiltY.resolution) {
+            mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution;
+        }
+
+        mOrientedRanges.haveTilt = true;
+
+        mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
+        mOrientedRanges.tilt.source = mSource;
+        mOrientedRanges.tilt.min = 0;
+        mOrientedRanges.tilt.max = M_PI_2;
+        mOrientedRanges.tilt.flat = 0;
+        mOrientedRanges.tilt.fuzz = 0;
+        mOrientedRanges.tilt.resolution = 0;
+    }
+
+    // Orientation
+    mOrientationScale = 0;
+    if (mHaveTilt) {
+        mOrientedRanges.haveOrientation = true;
+
+        mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+        mOrientedRanges.orientation.source = mSource;
+        mOrientedRanges.orientation.min = -M_PI;
+        mOrientedRanges.orientation.max = M_PI;
+        mOrientedRanges.orientation.flat = 0;
+        mOrientedRanges.orientation.fuzz = 0;
+        mOrientedRanges.orientation.resolution = 0;
+    } else if (mCalibration.orientationCalibration != Calibration::OrientationCalibration::NONE) {
+        if (mCalibration.orientationCalibration ==
+            Calibration::OrientationCalibration::INTERPOLATED) {
+            if (mRawPointerAxes.orientation.valid) {
+                if (mRawPointerAxes.orientation.maxValue > 0) {
+                    mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
+                } else if (mRawPointerAxes.orientation.minValue < 0) {
+                    mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
+                } else {
+                    mOrientationScale = 0;
+                }
+            }
+        }
+
+        mOrientedRanges.haveOrientation = true;
+
+        mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+        mOrientedRanges.orientation.source = mSource;
+        mOrientedRanges.orientation.min = -M_PI_2;
+        mOrientedRanges.orientation.max = M_PI_2;
+        mOrientedRanges.orientation.flat = 0;
+        mOrientedRanges.orientation.fuzz = 0;
+        mOrientedRanges.orientation.resolution = 0;
+    }
+
+    // Distance
+    mDistanceScale = 0;
+    if (mCalibration.distanceCalibration != Calibration::DistanceCalibration::NONE) {
+        if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::SCALED) {
+            if (mCalibration.haveDistanceScale) {
+                mDistanceScale = mCalibration.distanceScale;
+            } else {
+                mDistanceScale = 1.0f;
+            }
+        }
+
+        mOrientedRanges.haveDistance = true;
+
+        mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
+        mOrientedRanges.distance.source = mSource;
+        mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale;
+        mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale;
+        mOrientedRanges.distance.flat = 0;
+        mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale;
+        mOrientedRanges.distance.resolution = 0;
+    }
+
+    // Compute oriented precision, scales and ranges.
+    // Note that the maximum value reported is an inclusive maximum value so it is one
+    // unit less than the total width or height of the display.
+    switch (mInputDeviceOrientation) {
+        case DISPLAY_ORIENTATION_90:
+        case DISPLAY_ORIENTATION_270:
+            mOrientedXPrecision = mYPrecision;
+            mOrientedYPrecision = mXPrecision;
+
+            mOrientedRanges.x.min = 0;
+            mOrientedRanges.x.max = mDisplayHeight - 1;
+            mOrientedRanges.x.flat = 0;
+            mOrientedRanges.x.fuzz = 0;
+            mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
+
+            mOrientedRanges.y.min = 0;
+            mOrientedRanges.y.max = mDisplayWidth - 1;
+            mOrientedRanges.y.flat = 0;
+            mOrientedRanges.y.fuzz = 0;
+            mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
+            break;
+
+        default:
+            mOrientedXPrecision = mXPrecision;
+            mOrientedYPrecision = mYPrecision;
+
+            mOrientedRanges.x.min = 0;
+            mOrientedRanges.x.max = mDisplayWidth - 1;
+            mOrientedRanges.x.flat = 0;
+            mOrientedRanges.x.fuzz = 0;
+            mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
+
+            mOrientedRanges.y.min = 0;
+            mOrientedRanges.y.max = mDisplayHeight - 1;
+            mOrientedRanges.y.flat = 0;
+            mOrientedRanges.y.fuzz = 0;
+            mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
+            break;
+    }
+}
+
+void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
     DeviceMode oldDeviceMode = mDeviceMode;
 
     resolveExternalStylusPresence();
@@ -672,31 +931,28 @@
     }
 
     // Raw width and height in the natural orientation.
-    int32_t rawWidth = mRawPointerAxes.getRawWidth();
-    int32_t rawHeight = mRawPointerAxes.getRawHeight();
+    const int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    const int32_t rawHeight = mRawPointerAxes.getRawHeight();
 
-    bool viewportChanged = mViewport != *newViewport;
+    const bool viewportChanged = mViewport != *newViewport;
     bool skipViewportUpdate = false;
     if (viewportChanged) {
-        bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
+        const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
         mViewport = *newViewport;
 
         if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
-            // Convert rotated viewport to natural surface coordinates.
-            int32_t naturalLogicalWidth, naturalLogicalHeight;
+            // Convert rotated viewport to the natural orientation.
             int32_t naturalPhysicalWidth, naturalPhysicalHeight;
             int32_t naturalPhysicalLeft, naturalPhysicalTop;
             int32_t naturalDeviceWidth, naturalDeviceHeight;
 
-            // Apply the inverse of the input device orientation so that the surface is configured
-            // in the same orientation as the device. The input device orientation will be
-            // re-applied to mSurfaceOrientation.
-            const int32_t naturalSurfaceOrientation =
+            // Apply the inverse of the input device orientation so that the input device is
+            // configured in the same orientation as the viewport. The input device orientation will
+            // be re-applied by mInputDeviceOrientation.
+            const int32_t naturalDeviceOrientation =
                     (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
-            switch (naturalSurfaceOrientation) {
+            switch (naturalDeviceOrientation) {
                 case DISPLAY_ORIENTATION_90:
-                    naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
-                    naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
                     naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
                     naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
@@ -705,8 +961,6 @@
                     naturalDeviceHeight = mViewport.deviceWidth;
                     break;
                 case DISPLAY_ORIENTATION_180:
-                    naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
-                    naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
                     naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
                     naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
@@ -715,8 +969,6 @@
                     naturalDeviceHeight = mViewport.deviceHeight;
                     break;
                 case DISPLAY_ORIENTATION_270:
-                    naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
-                    naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
                     naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
                     naturalPhysicalLeft = mViewport.physicalTop;
@@ -726,8 +978,6 @@
                     break;
                 case DISPLAY_ORIENTATION_0:
                 default:
-                    naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
-                    naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
                     naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
                     naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalLeft = mViewport.physicalLeft;
@@ -748,49 +998,36 @@
             mPhysicalLeft = naturalPhysicalLeft;
             mPhysicalTop = naturalPhysicalTop;
 
-            const int32_t oldSurfaceWidth = mRawSurfaceWidth;
-            const int32_t oldSurfaceHeight = mRawSurfaceHeight;
-            mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
-            mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
-            mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
-            mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
-            mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
-            mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
+            const int32_t oldDisplayWidth = mDisplayWidth;
+            const int32_t oldDisplayHeight = mDisplayHeight;
+            mDisplayWidth = naturalDeviceWidth;
+            mDisplayHeight = naturalDeviceHeight;
 
-            if (isPerWindowInputRotationEnabled()) {
-                // When per-window input rotation is enabled, InputReader works in the un-rotated
-                // coordinate space, so we don't need to do anything if the device is already
-                // orientation-aware. If the device is not orientation-aware, then we need to apply
-                // the inverse rotation of the display so that when the display rotation is applied
-                // later as a part of the per-window transform, we get the expected screen
-                // coordinates.
-                mSurfaceOrientation = mParameters.orientationAware
-                        ? DISPLAY_ORIENTATION_0
-                        : getInverseRotation(mViewport.orientation);
-                // For orientation-aware devices that work in the un-rotated coordinate space, the
-                // viewport update should be skipped if it is only a change in the orientation.
-                skipViewportUpdate = mParameters.orientationAware &&
-                        mRawSurfaceWidth == oldSurfaceWidth &&
-                        mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged;
-            } else {
-                mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
-                                                                   : DISPLAY_ORIENTATION_0;
-            }
+            // InputReader works in the un-rotated display coordinate space, so we don't need to do
+            // anything if the device is already orientation-aware. If the device is not
+            // orientation-aware, then we need to apply the inverse rotation of the display so that
+            // when the display rotation is applied later as a part of the per-window transform, we
+            // get the expected screen coordinates.
+            mInputDeviceOrientation = mParameters.orientationAware
+                    ? DISPLAY_ORIENTATION_0
+                    : getInverseRotation(mViewport.orientation);
+            // For orientation-aware devices that work in the un-rotated coordinate space, the
+            // viewport update should be skipped if it is only a change in the orientation.
+            skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth &&
+                    mDisplayHeight == oldDisplayHeight && viewportOrientationChanged;
 
             // Apply the input device orientation for the device.
-            mSurfaceOrientation =
-                    (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
+            mInputDeviceOrientation =
+                    (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
         } else {
             mPhysicalWidth = rawWidth;
             mPhysicalHeight = rawHeight;
             mPhysicalLeft = 0;
             mPhysicalTop = 0;
 
-            mRawSurfaceWidth = rawWidth;
-            mRawSurfaceHeight = rawHeight;
-            mSurfaceLeft = 0;
-            mSurfaceTop = 0;
-            mSurfaceOrientation = DISPLAY_ORIENTATION_0;
+            mDisplayWidth = rawWidth;
+            mDisplayHeight = rawHeight;
+            mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
         }
     }
 
@@ -819,229 +1056,12 @@
     if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
         ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
               "display id %d",
-              getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight,
-              mSurfaceOrientation, mDeviceMode, mViewport.displayId);
-
-        // Configure X and Y factors.
-        mXScale = float(mRawSurfaceWidth) / rawWidth;
-        mYScale = float(mRawSurfaceHeight) / rawHeight;
-        mXTranslate = -mSurfaceLeft;
-        mYTranslate = -mSurfaceTop;
-        mXPrecision = 1.0f / mXScale;
-        mYPrecision = 1.0f / mYScale;
-
-        mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
-        mOrientedRanges.x.source = mSource;
-        mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
-        mOrientedRanges.y.source = mSource;
+              getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight,
+              mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
 
         configureVirtualKeys();
 
-        // Scale factor for terms that are not oriented in a particular axis.
-        // If the pixels are square then xScale == yScale otherwise we fake it
-        // by choosing an average.
-        mGeometricScale = avg(mXScale, mYScale);
-
-        // Size of diagonal axis.
-        float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
-
-        // Size factors.
-        if (mCalibration.sizeCalibration != Calibration::SizeCalibration::NONE) {
-            if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
-                mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
-            } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
-                mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
-            } else {
-                mSizeScale = 0.0f;
-            }
-
-            mOrientedRanges.haveTouchSize = true;
-            mOrientedRanges.haveToolSize = true;
-            mOrientedRanges.haveSize = true;
-
-            mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
-            mOrientedRanges.touchMajor.source = mSource;
-            mOrientedRanges.touchMajor.min = 0;
-            mOrientedRanges.touchMajor.max = diagonalSize;
-            mOrientedRanges.touchMajor.flat = 0;
-            mOrientedRanges.touchMajor.fuzz = 0;
-            mOrientedRanges.touchMajor.resolution = 0;
-
-            mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
-            mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
-
-            mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
-            mOrientedRanges.toolMajor.source = mSource;
-            mOrientedRanges.toolMajor.min = 0;
-            mOrientedRanges.toolMajor.max = diagonalSize;
-            mOrientedRanges.toolMajor.flat = 0;
-            mOrientedRanges.toolMajor.fuzz = 0;
-            mOrientedRanges.toolMajor.resolution = 0;
-
-            mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
-            mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
-
-            mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
-            mOrientedRanges.size.source = mSource;
-            mOrientedRanges.size.min = 0;
-            mOrientedRanges.size.max = 1.0;
-            mOrientedRanges.size.flat = 0;
-            mOrientedRanges.size.fuzz = 0;
-            mOrientedRanges.size.resolution = 0;
-        } else {
-            mSizeScale = 0.0f;
-        }
-
-        // Pressure factors.
-        mPressureScale = 0;
-        float pressureMax = 1.0;
-        if (mCalibration.pressureCalibration == Calibration::PressureCalibration::PHYSICAL ||
-            mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
-            if (mCalibration.havePressureScale) {
-                mPressureScale = mCalibration.pressureScale;
-                pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
-            } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
-                mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
-            }
-        }
-
-        mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
-        mOrientedRanges.pressure.source = mSource;
-        mOrientedRanges.pressure.min = 0;
-        mOrientedRanges.pressure.max = pressureMax;
-        mOrientedRanges.pressure.flat = 0;
-        mOrientedRanges.pressure.fuzz = 0;
-        mOrientedRanges.pressure.resolution = 0;
-
-        // Tilt
-        mTiltXCenter = 0;
-        mTiltXScale = 0;
-        mTiltYCenter = 0;
-        mTiltYScale = 0;
-        mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
-        if (mHaveTilt) {
-            mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue);
-            mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue);
-            mTiltXScale = M_PI / 180;
-            mTiltYScale = M_PI / 180;
-
-            if (mRawPointerAxes.tiltX.resolution) {
-                mTiltXScale = 1.0 / mRawPointerAxes.tiltX.resolution;
-            }
-            if (mRawPointerAxes.tiltY.resolution) {
-                mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution;
-            }
-
-            mOrientedRanges.haveTilt = true;
-
-            mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
-            mOrientedRanges.tilt.source = mSource;
-            mOrientedRanges.tilt.min = 0;
-            mOrientedRanges.tilt.max = M_PI_2;
-            mOrientedRanges.tilt.flat = 0;
-            mOrientedRanges.tilt.fuzz = 0;
-            mOrientedRanges.tilt.resolution = 0;
-        }
-
-        // Orientation
-        mOrientationScale = 0;
-        if (mHaveTilt) {
-            mOrientedRanges.haveOrientation = true;
-
-            mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
-            mOrientedRanges.orientation.source = mSource;
-            mOrientedRanges.orientation.min = -M_PI;
-            mOrientedRanges.orientation.max = M_PI;
-            mOrientedRanges.orientation.flat = 0;
-            mOrientedRanges.orientation.fuzz = 0;
-            mOrientedRanges.orientation.resolution = 0;
-        } else if (mCalibration.orientationCalibration !=
-                   Calibration::OrientationCalibration::NONE) {
-            if (mCalibration.orientationCalibration ==
-                Calibration::OrientationCalibration::INTERPOLATED) {
-                if (mRawPointerAxes.orientation.valid) {
-                    if (mRawPointerAxes.orientation.maxValue > 0) {
-                        mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
-                    } else if (mRawPointerAxes.orientation.minValue < 0) {
-                        mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
-                    } else {
-                        mOrientationScale = 0;
-                    }
-                }
-            }
-
-            mOrientedRanges.haveOrientation = true;
-
-            mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
-            mOrientedRanges.orientation.source = mSource;
-            mOrientedRanges.orientation.min = -M_PI_2;
-            mOrientedRanges.orientation.max = M_PI_2;
-            mOrientedRanges.orientation.flat = 0;
-            mOrientedRanges.orientation.fuzz = 0;
-            mOrientedRanges.orientation.resolution = 0;
-        }
-
-        // Distance
-        mDistanceScale = 0;
-        if (mCalibration.distanceCalibration != Calibration::DistanceCalibration::NONE) {
-            if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::SCALED) {
-                if (mCalibration.haveDistanceScale) {
-                    mDistanceScale = mCalibration.distanceScale;
-                } else {
-                    mDistanceScale = 1.0f;
-                }
-            }
-
-            mOrientedRanges.haveDistance = true;
-
-            mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
-            mOrientedRanges.distance.source = mSource;
-            mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale;
-            mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale;
-            mOrientedRanges.distance.flat = 0;
-            mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale;
-            mOrientedRanges.distance.resolution = 0;
-        }
-
-        // Compute oriented precision, scales and ranges.
-        // Note that the maximum value reported is an inclusive maximum value so it is one
-        // unit less than the total width or height of surface.
-        switch (mSurfaceOrientation) {
-            case DISPLAY_ORIENTATION_90:
-            case DISPLAY_ORIENTATION_270:
-                mOrientedXPrecision = mYPrecision;
-                mOrientedYPrecision = mXPrecision;
-
-                mOrientedRanges.x.min = mYTranslate;
-                mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1;
-                mOrientedRanges.x.flat = 0;
-                mOrientedRanges.x.fuzz = 0;
-                mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
-
-                mOrientedRanges.y.min = mXTranslate;
-                mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1;
-                mOrientedRanges.y.flat = 0;
-                mOrientedRanges.y.fuzz = 0;
-                mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
-                break;
-
-            default:
-                mOrientedXPrecision = mXPrecision;
-                mOrientedYPrecision = mYPrecision;
-
-                mOrientedRanges.x.min = mXTranslate;
-                mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1;
-                mOrientedRanges.x.flat = 0;
-                mOrientedRanges.x.fuzz = 0;
-                mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
-
-                mOrientedRanges.y.min = mYTranslate;
-                mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1;
-                mOrientedRanges.y.flat = 0;
-                mOrientedRanges.y.fuzz = 0;
-                mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
-                break;
-        }
+        initializeOrientedRanges();
 
         // Location
         updateAffineTransformation();
@@ -1049,7 +1069,7 @@
         if (mDeviceMode == DeviceMode::POINTER) {
             // Compute pointer gesture detection parameters.
             float rawDiagonal = hypotf(rawWidth, rawHeight);
-            float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
+            float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight);
 
             // Scale movements such that one whole swipe of the touch pad covers a
             // given area relative to the diagonal size of the display when no acceleration
@@ -1083,19 +1103,15 @@
     }
 }
 
-void TouchInputMapper::dumpSurface(std::string& dump) {
+void TouchInputMapper::dumpDisplay(std::string& dump) {
     dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
-    dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth);
-    dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight);
-    dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
-    dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
-    dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight);
-    dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom);
+    dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth);
+    dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight);
     dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
     dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
     dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
     dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
-    dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
+    dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
 }
 
 void TouchInputMapper::configureVirtualKeys() {
@@ -1134,16 +1150,16 @@
         int32_t halfHeight = virtualKeyDefinition.height / 2;
 
         virtualKey.hitLeft =
-                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth +
+                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth +
                 touchScreenLeft;
         virtualKey.hitRight =
-                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth +
+                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth +
                 touchScreenLeft;
-        virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
-                        mRawSurfaceHeight +
+        virtualKey.hitTop =
+                (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight +
                 touchScreenTop;
-        virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
-                        mRawSurfaceHeight +
+        virtualKey.hitBottom =
+                (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight +
                 touchScreenTop;
         mVirtualKeys.push_back(virtualKey);
     }
@@ -1300,26 +1316,8 @@
 void TouchInputMapper::dumpCalibration(std::string& dump) {
     dump += INDENT3 "Calibration:\n";
 
-    // Size
-    switch (mCalibration.sizeCalibration) {
-        case Calibration::SizeCalibration::NONE:
-            dump += INDENT4 "touch.size.calibration: none\n";
-            break;
-        case Calibration::SizeCalibration::GEOMETRIC:
-            dump += INDENT4 "touch.size.calibration: geometric\n";
-            break;
-        case Calibration::SizeCalibration::DIAMETER:
-            dump += INDENT4 "touch.size.calibration: diameter\n";
-            break;
-        case Calibration::SizeCalibration::BOX:
-            dump += INDENT4 "touch.size.calibration: box\n";
-            break;
-        case Calibration::SizeCalibration::AREA:
-            dump += INDENT4 "touch.size.calibration: area\n";
-            break;
-        default:
-            ALOG_ASSERT(false);
-    }
+    dump += INDENT4 "touch.size.calibration: ";
+    dump += ftl::enum_string(mCalibration.sizeCalibration) + "\n";
 
     if (mCalibration.haveSizeScale) {
         dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale);
@@ -1409,7 +1407,7 @@
 
 void TouchInputMapper::updateAffineTransformation() {
     mAffineTransform = getPolicy()->getTouchAffineTransformation(getDeviceContext().getDescriptor(),
-                                                                 mSurfaceOrientation);
+                                                                 mInputDeviceOrientation);
 }
 
 void TouchInputMapper::reset(nsecs_t when) {
@@ -1498,14 +1496,14 @@
         assignPointerIds(last, next);
     }
 
-#if DEBUG_RAW_EVENTS
-    ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
-          "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
-          last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
-          last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
-          last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
-          next.rawPointerData.canceledIdBits.value);
-#endif
+    if (DEBUG_RAW_EVENTS) {
+        ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
+              "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
+              last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
+              last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
+              last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
+              next.rawPointerData.canceledIdBits.value);
+    }
 
     if (!next.rawPointerData.touchingIdBits.isEmpty() &&
         !next.rawPointerData.hoveringIdBits.isEmpty() &&
@@ -1559,9 +1557,9 @@
             nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
             clearStylusDataPendingFlags();
             mCurrentRawState.copyFrom(mLastRawState);
-#if DEBUG_STYLUS_FUSION
-            ALOGD("Timeout expired, synthesizing event with new stylus data");
-#endif
+            if (DEBUG_STYLUS_FUSION) {
+                ALOGD("Timeout expired, synthesizing event with new stylus data");
+            }
             const nsecs_t readTime = when; // consider this synthetic event to be zero latency
             cookAndDispatch(when, readTime);
         } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
@@ -1701,9 +1699,10 @@
     mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
 
     mPointerController->setButtonState(mCurrentRawState.buttonState);
-    setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
-                  mCurrentCookedState.cookedPointerData.idToIndex,
-                  mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId);
+    mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+                                 mCurrentCookedState.cookedPointerData.idToIndex,
+                                 mCurrentCookedState.cookedPointerData.touchingIdBits,
+                                 mViewport.displayId);
 }
 
 bool TouchInputMapper::isTouchScreen() {
@@ -1747,24 +1746,24 @@
             state.rawPointerData.pointerCount != 0;
     if (initialDown) {
         if (mExternalStylusState.pressure != 0.0f) {
-#if DEBUG_STYLUS_FUSION
-            ALOGD("Have both stylus and touch data, beginning fusion");
-#endif
+            if (DEBUG_STYLUS_FUSION) {
+                ALOGD("Have both stylus and touch data, beginning fusion");
+            }
             mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
         } else if (timeout) {
-#if DEBUG_STYLUS_FUSION
-            ALOGD("Timeout expired, assuming touch is not a stylus.");
-#endif
+            if (DEBUG_STYLUS_FUSION) {
+                ALOGD("Timeout expired, assuming touch is not a stylus.");
+            }
             resetExternalStylus();
         } else {
             if (mExternalStylusFusionTimeout == LLONG_MAX) {
                 mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
             }
-#if DEBUG_STYLUS_FUSION
-            ALOGD("No stylus data but stylus is connected, requesting timeout "
-                  "(%" PRId64 "ms)",
-                  mExternalStylusFusionTimeout);
-#endif
+            if (DEBUG_STYLUS_FUSION) {
+                ALOGD("No stylus data but stylus is connected, requesting timeout "
+                      "(%" PRId64 "ms)",
+                      mExternalStylusFusionTimeout);
+            }
             getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
             return true;
         }
@@ -1772,9 +1771,9 @@
 
     // Check if the stylus pointer has gone up.
     if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
-#if DEBUG_STYLUS_FUSION
-        ALOGD("Stylus pointer is going up");
-#endif
+        if (DEBUG_STYLUS_FUSION) {
+            ALOGD("Stylus pointer is going up");
+        }
         mExternalStylusId = -1;
     }
 
@@ -1815,10 +1814,10 @@
             // Pointer went up while virtual key was down.
             mCurrentVirtualKey.down = false;
             if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
-                ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
-                      mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
+                if (DEBUG_VIRTUAL_KEYS) {
+                    ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+                          mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+                }
                 dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
                                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
             }
@@ -1842,10 +1841,10 @@
         // into the main display surface.
         mCurrentVirtualKey.down = false;
         if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
-            ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
-                  mCurrentVirtualKey.scanCode);
-#endif
+            if (DEBUG_VIRTUAL_KEYS) {
+                ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+                      mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+            }
             dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
                                AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
                                        AKEY_EVENT_FLAG_CANCELED);
@@ -1857,8 +1856,10 @@
         // Pointer just went down.  Check for virtual key press or off-screen touches.
         uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
         const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
-        // Exclude unscaled device for inside surface checking.
-        if (!isPointInsideSurface(pointer.x, pointer.y) && mDeviceMode != DeviceMode::UNSCALED) {
+        // Skip checking whether the pointer is inside the physical frame if the device is in
+        // unscaled mode.
+        if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
+            mDeviceMode != DeviceMode::UNSCALED) {
             // If exactly one pointer went down, check for virtual key hit.
             // Otherwise we will drop the entire stroke.
             if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1873,10 +1874,10 @@
                                                                virtualKey->scanCode);
 
                     if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
-                        ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
-                              mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
+                        if (DEBUG_VIRTUAL_KEYS) {
+                            ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+                                  mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+                        }
                         dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
                                            AKEY_EVENT_FLAG_FROM_SYSTEM |
                                                    AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
@@ -1919,7 +1920,7 @@
     NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
                        AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
                        keyEventFlags, keyCode, scanCode, metaState, downTime);
-    getListener()->notifyKey(&args);
+    getListener().notifyKey(&args);
 }
 
 void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
@@ -2127,7 +2128,7 @@
     }
 
     // Walk through the the active pointers and map device coordinates onto
-    // surface coordinates and adjust for display orientation.
+    // display coordinates and adjust for display orientation.
     for (uint32_t i = 0; i < currentPointerCount; i++) {
         const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
 
@@ -2287,15 +2288,15 @@
         mAffineTransform.applyTo(xTransformed, yTransformed);
         rotateAndScale(xTransformed, yTransformed);
 
-        // Adjust X, Y, and coverage coords for surface orientation.
+        // Adjust X, Y, and coverage coords for input device orientation.
         float left, top, right, bottom;
 
-        switch (mSurfaceOrientation) {
+        switch (mInputDeviceOrientation) {
             case DISPLAY_ORIENTATION_90:
-                left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-                right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-                bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
-                top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
+                left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
+                right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
+                bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
+                top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
                 orientation -= M_PI_2;
                 if (mOrientedRanges.haveOrientation &&
                     orientation < mOrientedRanges.orientation.min) {
@@ -2306,8 +2307,8 @@
             case DISPLAY_ORIENTATION_180:
                 left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
                 right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
-                bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
-                top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
+                bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
+                top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
                 orientation -= M_PI;
                 if (mOrientedRanges.haveOrientation &&
                     orientation < mOrientedRanges.orientation.min) {
@@ -2318,8 +2319,8 @@
             case DISPLAY_ORIENTATION_270:
                 left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
                 right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
-                bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-                top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
+                top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale;
                 orientation += M_PI_2;
                 if (mOrientedRanges.haveOrientation &&
                     orientation > mOrientedRanges.orientation.max) {
@@ -2328,10 +2329,10 @@
                 }
                 break;
             default:
-                left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-                right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-                bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-                top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale;
+                right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
+                bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
+                top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
                 break;
         }
 
@@ -2441,9 +2442,10 @@
         }
 
         if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
-            setTouchSpots(mPointerGesture.currentGestureCoords,
-                          mPointerGesture.currentGestureIdToIndex,
-                          mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId());
+            mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+                                         mPointerGesture.currentGestureIdToIndex,
+                                         mPointerGesture.currentGestureIdBits,
+                                         mPointerController->getDisplayId());
         }
     } else {
         mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -2593,7 +2595,8 @@
         // the pointer is hovering again even if the user is not currently touching
         // the touch pad.  This ensures that a view will receive a fresh hover enter
         // event after a tap.
-        auto [x, y] = getMouseCursorPosition();
+        float x, y;
+        mPointerController->getPosition(&x, &y);
 
         PointerProperties pointerProperties;
         pointerProperties.clear();
@@ -2611,7 +2614,7 @@
                               metaState, buttonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
                               0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getListener().notifyMotion(&args);
     }
 
     // Update state.
@@ -2662,9 +2665,9 @@
 
     // Handle TAP timeout.
     if (isTimeout) {
-#if DEBUG_GESTURES
-        ALOGD("Gestures: Processing timeout");
-#endif
+        if (DEBUG_GESTURES) {
+            ALOGD("Gestures: Processing timeout");
+        }
 
         if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
             if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
@@ -2673,9 +2676,9 @@
                                                    mConfig.pointerGestureTapDragInterval);
             } else {
                 // The tap is finished.
-#if DEBUG_GESTURES
-                ALOGD("Gestures: TAP finished");
-#endif
+                if (DEBUG_GESTURES) {
+                    ALOGD("Gestures: TAP finished");
+                }
                 *outFinishPreviousGesture = true;
 
                 mPointerGesture.activeGestureId = -1;
@@ -2771,10 +2774,11 @@
     // Switch states based on button and pointer state.
     if (isQuietTime) {
         // Case 1: Quiet time. (QUIET)
-#if DEBUG_GESTURES
-        ALOGD("Gestures: QUIET for next %0.3fms",
-              (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
-#endif
+        if (DEBUG_GESTURES) {
+            ALOGD("Gestures: QUIET for next %0.3fms",
+                  (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) *
+                          0.000001f);
+        }
         if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) {
             *outFinishPreviousGesture = true;
         }
@@ -2798,11 +2802,11 @@
         // active.  If the user first puts one finger down to click then adds another
         // finger to drag then the active pointer should switch to the finger that is
         // being dragged.
-#if DEBUG_GESTURES
-        ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
-              "currentFingerCount=%d",
-              activeTouchId, currentFingerCount);
-#endif
+        if (DEBUG_GESTURES) {
+            ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
+                  "currentFingerCount=%d",
+                  activeTouchId, currentFingerCount);
+        }
         // Reset state when just starting.
         if (mPointerGesture.lastGestureMode != PointerGesture::Mode::BUTTON_CLICK_OR_DRAG) {
             *outFinishPreviousGesture = true;
@@ -2827,11 +2831,11 @@
             }
             if (bestId >= 0 && bestId != activeTouchId) {
                 mPointerGesture.activeTouchId = activeTouchId = bestId;
-#if DEBUG_GESTURES
-                ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
-                      "bestId=%d, bestSpeed=%0.3f",
-                      bestId, bestSpeed);
-#endif
+                if (DEBUG_GESTURES) {
+                    ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
+                          "bestId=%d, bestSpeed=%0.3f",
+                          bestId, bestSpeed);
+                }
             }
         }
 
@@ -2844,18 +2848,19 @@
             deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
             deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
 
-            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+            rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
             mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
             // Move the pointer using a relative motion.
             // When using spots, the click will occur at the position of the anchor
             // spot and all other spots will move there.
-            moveMouseCursor(deltaX, deltaY);
+            mPointerController->move(deltaX, deltaY);
         } else {
             mPointerVelocityControl.reset();
         }
 
-        auto [x, y] = getMouseCursorPosition();
+        float x, y;
+        mPointerController->getPosition(&x, &y);
 
         mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
         mPointerGesture.currentGestureIdBits.clear();
@@ -2881,12 +2886,13 @@
              mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
             lastFingerCount == 1) {
             if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
-                auto [x, y] = getMouseCursorPosition();
+                float x, y;
+                mPointerController->getPosition(&x, &y);
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: TAP");
-#endif
+                    if (DEBUG_GESTURES) {
+                        ALOGD("Gestures: TAP");
+                    }
 
                     mPointerGesture.tapUpTime = when;
                     getContext()->requestTimeoutAtTime(when +
@@ -2912,29 +2918,29 @@
 
                     tapped = true;
                 } else {
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
-                          y - mPointerGesture.tapY);
-#endif
+                    if (DEBUG_GESTURES) {
+                        ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
+                              y - mPointerGesture.tapY);
+                    }
                 }
             } else {
-#if DEBUG_GESTURES
-                if (mPointerGesture.tapDownTime != LLONG_MIN) {
-                    ALOGD("Gestures: Not a TAP, %0.3fms since down",
-                          (when - mPointerGesture.tapDownTime) * 0.000001f);
-                } else {
-                    ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+                if (DEBUG_GESTURES) {
+                    if (mPointerGesture.tapDownTime != LLONG_MIN) {
+                        ALOGD("Gestures: Not a TAP, %0.3fms since down",
+                              (when - mPointerGesture.tapDownTime) * 0.000001f);
+                    } else {
+                        ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+                    }
                 }
-#endif
             }
         }
 
         mPointerVelocityControl.reset();
 
         if (!tapped) {
-#if DEBUG_GESTURES
-            ALOGD("Gestures: NEUTRAL");
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: NEUTRAL");
+            }
             mPointerGesture.activeGestureId = -1;
             mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
             mPointerGesture.currentGestureIdBits.clear();
@@ -2949,21 +2955,22 @@
         mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
         if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
             if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
-                auto [x, y] = getMouseCursorPosition();
+                float x, y;
+                mPointerController->getPosition(&x, &y);
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
                     mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
                 } else {
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
-                          x - mPointerGesture.tapX, y - mPointerGesture.tapY);
-#endif
+                    if (DEBUG_GESTURES) {
+                        ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
+                              x - mPointerGesture.tapX, y - mPointerGesture.tapY);
+                    }
                 }
             } else {
-#if DEBUG_GESTURES
-                ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
-                      (when - mPointerGesture.tapUpTime) * 0.000001f);
-#endif
+                if (DEBUG_GESTURES) {
+                    ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
+                          (when - mPointerGesture.tapUpTime) * 0.000001f);
+                }
             }
         } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) {
             mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -2978,26 +2985,26 @@
             deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
             deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
 
-            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+            rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
             mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
             // Move the pointer using a relative motion.
             // When using spots, the hover or drag will occur at the position of the anchor spot.
-            moveMouseCursor(deltaX, deltaY);
+            mPointerController->move(deltaX, deltaY);
         } else {
             mPointerVelocityControl.reset();
         }
 
         bool down;
         if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) {
-#if DEBUG_GESTURES
-            ALOGD("Gestures: TAP_DRAG");
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: TAP_DRAG");
+            }
             down = true;
         } else {
-#if DEBUG_GESTURES
-            ALOGD("Gestures: HOVER");
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: HOVER");
+            }
             if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) {
                 *outFinishPreviousGesture = true;
             }
@@ -3005,7 +3012,8 @@
             down = false;
         }
 
-        auto [x, y] = getMouseCursorPosition();
+        float x, y;
+        mPointerController->getPosition(&x, &y);
 
         mPointerGesture.currentGestureIdBits.clear();
         mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3050,12 +3058,13 @@
         } else if (!settled && currentFingerCount > lastFingerCount) {
             // Additional pointers have gone down but not yet settled.
             // Reset the gesture.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
-                  "settle time remaining %0.3fms",
-                  (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
-                   when) * 0.000001f);
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: Resetting gesture since additional pointers went down for "
+                      "MULTITOUCH, settle time remaining %0.3fms",
+                      (mPointerGesture.firstTouchTime +
+                       mConfig.pointerGestureMultitouchSettleInterval - when) *
+                              0.000001f);
+            }
             *outCancelPreviousGesture = true;
         } else {
             // Continue previous gesture.
@@ -3069,18 +3078,18 @@
             mPointerVelocityControl.reset();
 
             // Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
-                  "settle time remaining %0.3fms",
-                  (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
-                   when) * 0.000001f);
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+                      "settle time remaining %0.3fms",
+                      (mPointerGesture.firstTouchTime +
+                       mConfig.pointerGestureMultitouchSettleInterval - when) *
+                              0.000001f);
+            }
             mCurrentRawState.rawPointerData
                     .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
                                                    &mPointerGesture.referenceTouchY);
-            auto [x, y] = getMouseCursorPosition();
-            mPointerGesture.referenceGestureX = x;
-            mPointerGesture.referenceGestureY = y;
+            mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+                                            &mPointerGesture.referenceGestureY);
         }
 
         // Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3133,10 +3142,10 @@
             if (distOverThreshold >= 2) {
                 if (currentFingerCount > 2) {
                     // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
-                          currentFingerCount);
-#endif
+                    if (DEBUG_GESTURES) {
+                        ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+                              currentFingerCount);
+                    }
                     *outCancelPreviousGesture = true;
                     mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                 } else {
@@ -3152,10 +3161,11 @@
                     if (mutualDistance > mPointerGestureMaxSwipeWidth) {
                         // There are two pointers but they are too far apart for a SWIPE,
                         // switch to FREEFORM.
-#if DEBUG_GESTURES
-                        ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
-                              mutualDistance, mPointerGestureMaxSwipeWidth);
-#endif
+                        if (DEBUG_GESTURES) {
+                            ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > "
+                                  "%0.3f",
+                                  mutualDistance, mPointerGestureMaxSwipeWidth);
+                        }
                         *outCancelPreviousGesture = true;
                         mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                     } else {
@@ -3180,25 +3190,25 @@
                             float cosine = dot / (dist1 * dist2); // denominator always > 0
                             if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
                                 // Pointers are moving in the same direction.  Switch to SWIPE.
-#if DEBUG_GESTURES
-                                ALOGD("Gestures: PRESS transitioned to SWIPE, "
-                                      "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
-                                      "cosine %0.3f >= %0.3f",
-                                      dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
-                                      mConfig.pointerGestureMultitouchMinDistance, cosine,
-                                      mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
+                                if (DEBUG_GESTURES) {
+                                    ALOGD("Gestures: PRESS transitioned to SWIPE, "
+                                          "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                          "cosine %0.3f >= %0.3f",
+                                          dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+                                          mConfig.pointerGestureMultitouchMinDistance, cosine,
+                                          mConfig.pointerGestureSwipeTransitionAngleCosine);
+                                }
                                 mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
                             } else {
                                 // Pointers are moving in different directions.  Switch to FREEFORM.
-#if DEBUG_GESTURES
-                                ALOGD("Gestures: PRESS transitioned to FREEFORM, "
-                                      "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
-                                      "cosine %0.3f < %0.3f",
-                                      dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
-                                      mConfig.pointerGestureMultitouchMinDistance, cosine,
-                                      mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
+                                if (DEBUG_GESTURES) {
+                                    ALOGD("Gestures: PRESS transitioned to FREEFORM, "
+                                          "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                          "cosine %0.3f < %0.3f",
+                                          dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+                                          mConfig.pointerGestureMultitouchMinDistance, cosine,
+                                          mConfig.pointerGestureSwipeTransitionAngleCosine);
+                                }
                                 *outCancelPreviousGesture = true;
                                 mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                             }
@@ -3210,10 +3220,10 @@
             // Switch from SWIPE to FREEFORM if additional pointers go down.
             // Cancel previous gesture.
             if (currentFingerCount > 2) {
-#if DEBUG_GESTURES
-                ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
-                      currentFingerCount);
-#endif
+                if (DEBUG_GESTURES) {
+                    ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+                          currentFingerCount);
+                }
                 *outCancelPreviousGesture = true;
                 mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
             }
@@ -3236,7 +3246,7 @@
             commonDeltaX *= mPointerXMovementScale;
             commonDeltaY *= mPointerYMovementScale;
 
-            rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
+            rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
             mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
 
             mPointerGesture.referenceGestureX += commonDeltaX;
@@ -3247,11 +3257,11 @@
         if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
             mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
             // PRESS or SWIPE mode.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
-                  "activeGestureId=%d, currentTouchPointerCount=%d",
-                  activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
+                      "activeGestureId=%d, currentTouchPointerCount=%d",
+                      activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+            }
             ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
             mPointerGesture.currentGestureIdBits.clear();
@@ -3268,11 +3278,11 @@
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
         } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
             // FREEFORM mode.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: FREEFORM activeTouchId=%d,"
-                  "activeGestureId=%d, currentTouchPointerCount=%d",
-                  activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: FREEFORM activeTouchId=%d,"
+                      "activeGestureId=%d, currentTouchPointerCount=%d",
+                      activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+            }
             ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
             mPointerGesture.currentGestureIdBits.clear();
@@ -3311,13 +3321,13 @@
                 }
             }
 
-#if DEBUG_GESTURES
-            ALOGD("Gestures: FREEFORM follow up "
-                  "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
-                  "activeGestureId=%d",
-                  mappedTouchIdBits.value, usedGestureIdBits.value,
-                  mPointerGesture.activeGestureId);
-#endif
+            if (DEBUG_GESTURES) {
+                ALOGD("Gestures: FREEFORM follow up "
+                      "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+                      "activeGestureId=%d",
+                      mappedTouchIdBits.value, usedGestureIdBits.value,
+                      mPointerGesture.activeGestureId);
+            }
 
             BitSet32 idBits(mCurrentCookedState.fingerIdBits);
             for (uint32_t i = 0; i < currentFingerCount; i++) {
@@ -3326,18 +3336,18 @@
                 if (!mappedTouchIdBits.hasBit(touchId)) {
                     gestureId = usedGestureIdBits.markFirstUnmarkedBit();
                     mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: FREEFORM "
-                          "new mapping for touch id %d -> gesture id %d",
-                          touchId, gestureId);
-#endif
+                    if (DEBUG_GESTURES) {
+                        ALOGD("Gestures: FREEFORM "
+                              "new mapping for touch id %d -> gesture id %d",
+                              touchId, gestureId);
+                    }
                 } else {
                     gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: FREEFORM "
-                          "existing mapping for touch id %d -> gesture id %d",
-                          touchId, gestureId);
-#endif
+                    if (DEBUG_GESTURES) {
+                        ALOGD("Gestures: FREEFORM "
+                              "existing mapping for touch id %d -> gesture id %d",
+                              touchId, gestureId);
+                    }
                 }
                 mPointerGesture.currentGestureIdBits.markBit(gestureId);
                 mPointerGesture.currentGestureIdToIndex[gestureId] = i;
@@ -3346,7 +3356,7 @@
                         mCurrentRawState.rawPointerData.pointerForId(touchId);
                 float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
                 float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
-                rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+                rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
 
                 mPointerGesture.currentGestureProperties[i].clear();
                 mPointerGesture.currentGestureProperties[i].id = gestureId;
@@ -3366,47 +3376,46 @@
             if (mPointerGesture.activeGestureId < 0) {
                 mPointerGesture.activeGestureId =
                         mPointerGesture.currentGestureIdBits.firstMarkedBit();
-#if DEBUG_GESTURES
-                ALOGD("Gestures: FREEFORM new "
-                      "activeGestureId=%d",
-                      mPointerGesture.activeGestureId);
-#endif
+                if (DEBUG_GESTURES) {
+                    ALOGD("Gestures: FREEFORM new activeGestureId=%d",
+                          mPointerGesture.activeGestureId);
+                }
             }
         }
     }
 
     mPointerController->setButtonState(mCurrentRawState.buttonState);
 
-#if DEBUG_GESTURES
-    ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
-          "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
-          "lastGestureMode=%d, lastGestureIdBits=0x%08x",
-          toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
-          mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
-          mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
-    for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
-        const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
-        const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
-        ALOGD("  currentGesture[%d]: index=%d, toolType=%d, "
-              "x=%0.3f, y=%0.3f, pressure=%0.3f",
-              id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
-              coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
-              coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+    if (DEBUG_GESTURES) {
+        ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
+              "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
+              "lastGestureMode=%d, lastGestureIdBits=0x%08x",
+              toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
+              mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
+              mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
+        for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+            const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
+            const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
+            ALOGD("  currentGesture[%d]: index=%d, toolType=%d, "
+                  "x=%0.3f, y=%0.3f, pressure=%0.3f",
+                  id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+                  coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+                  coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+        }
+        for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
+            const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
+            const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
+            ALOGD("  lastGesture[%d]: index=%d, toolType=%d, "
+                  "x=%0.3f, y=%0.3f, pressure=%0.3f",
+                  id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+                  coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+                  coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+        }
     }
-    for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
-        const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
-        const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
-        ALOGD("  lastGesture[%d]: index=%d, toolType=%d, "
-              "x=%0.3f, y=%0.3f, pressure=%0.3f",
-              id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
-              coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
-              coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
-    }
-#endif
     return true;
 }
 
@@ -3418,13 +3427,15 @@
     if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
         uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
         uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
-        setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
-                               mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
+        mPointerController
+                ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
+                              mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
 
         hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
         down = !hovering;
 
-        auto [x, y] = getMouseCursorPosition();
+        float x, y;
+        mPointerController->getPosition(&x, &y);
         mPointerSimple.currentCoords.copyFrom(
                 mCurrentCookedState.cookedPointerData.pointerCoords[index]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3462,10 +3473,10 @@
                       mLastRawState.rawPointerData.pointers[lastIndex].y) *
                     mPointerYMovementScale;
 
-            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+            rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
             mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
-            moveMouseCursor(deltaX, deltaY);
+            mPointerController->move(deltaX, deltaY);
         } else {
             mPointerVelocityControl.reset();
         }
@@ -3473,7 +3484,8 @@
         down = isPointerDown(mCurrentRawState.buttonState);
         hovering = !down;
 
-        auto [x, y] = getMouseCursorPosition();
+        float x, y;
+        mPointerController->getPosition(&x, &y);
         mPointerSimple.currentCoords.copyFrom(
                 mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3513,7 +3525,8 @@
     }
     int32_t displayId = mPointerController->getDisplayId();
 
-    auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition();
+    float xCursorPosition, yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
@@ -3526,7 +3539,7 @@
                               &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
                               xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getListener().notifyMotion(&args);
     }
 
     if (mPointerSimple.hovering && !hovering) {
@@ -3540,7 +3553,7 @@
                               &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
                               xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getListener().notifyMotion(&args);
     }
 
     if (down) {
@@ -3556,7 +3569,7 @@
                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&args);
+            getListener().notifyMotion(&args);
         }
 
         // Send move.
@@ -3567,7 +3580,7 @@
                               &mPointerSimple.currentCoords, mOrientedXPrecision,
                               mOrientedYPrecision, xCursorPosition, yCursorPosition,
                               mPointerSimple.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getListener().notifyMotion(&args);
     }
 
     if (hovering) {
@@ -3582,7 +3595,7 @@
                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&args);
+            getListener().notifyMotion(&args);
         }
 
         // Send hover move.
@@ -3593,7 +3606,7 @@
                               &mPointerSimple.currentCoords, mOrientedXPrecision,
                               mOrientedYPrecision, xCursorPosition, yCursorPosition,
                               mPointerSimple.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getListener().notifyMotion(&args);
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3615,7 +3628,7 @@
                               &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
                               xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getListener().notifyMotion(&args);
     }
 
     // Save state.
@@ -3679,21 +3692,19 @@
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mDeviceMode == DeviceMode::POINTER) {
-        auto [x, y] = getMouseCursorPosition();
-        xCursorPosition = x;
-        yCursorPosition = y;
+        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
     }
     const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
-                  [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
+                  [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
     NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
                           policyFlags, action, actionButton, flags, metaState, buttonState,
                           MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
                           pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                           downTime, std::move(frames));
-    getListener()->notifyMotion(&args);
+    getListener().notifyMotion(&args);
 }
 
 bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
@@ -3731,60 +3742,59 @@
     abortTouches(when, readTime, 0 /* policyFlags*/);
 }
 
-// Transform raw coordinate to surface coordinate
-void TouchInputMapper::rotateAndScale(float& x, float& y) {
-    // Scale to surface coordinate.
+// Transform input device coordinates to display panel coordinates.
+void TouchInputMapper::rotateAndScale(float& x, float& y) const {
     const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
     const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
 
     const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale;
     const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale;
 
-    // Rotate to surface coordinate.
+    // Rotate to display coordinate.
     // 0 - no swap and reverse.
     // 90 - swap x/y and reverse y.
     // 180 - reverse x, y.
     // 270 - swap x/y and reverse x.
-    switch (mSurfaceOrientation) {
+    switch (mInputDeviceOrientation) {
         case DISPLAY_ORIENTATION_0:
-            x = xScaled + mXTranslate;
-            y = yScaled + mYTranslate;
+            x = xScaled;
+            y = yScaled;
             break;
         case DISPLAY_ORIENTATION_90:
-            y = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
-            x = yScaled + mYTranslate;
+            y = xScaledMax;
+            x = yScaled;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
-            y = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
+            x = xScaledMax;
+            y = yScaledMax;
             break;
         case DISPLAY_ORIENTATION_270:
-            y = xScaled + mXTranslate;
-            x = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
+            y = xScaled;
+            x = yScaledMax;
             break;
         default:
             assert(false);
     }
 }
 
-bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const {
     const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
     const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
 
     return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
-            xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
+            xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
             y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
-            yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom;
+            yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
 }
 
 const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
     for (const VirtualKey& virtualKey : mVirtualKeys) {
-#if DEBUG_VIRTUAL_KEYS
-        ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
-              "left=%d, top=%d, right=%d, bottom=%d",
-              x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop,
-              virtualKey.hitRight, virtualKey.hitBottom);
-#endif
+        if (DEBUG_VIRTUAL_KEYS) {
+            ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+                  "left=%d, top=%d, right=%d, bottom=%d",
+                  x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft,
+                  virtualKey.hitTop, virtualKey.hitRight, virtualKey.hitBottom);
+        }
 
         if (virtualKey.isHit(x, y)) {
             return &virtualKey;
@@ -3881,13 +3891,13 @@
         }
     }
 
-#if DEBUG_POINTER_ASSIGNMENT
-    ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
-    for (size_t i = 0; i < heapSize; i++) {
-        ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
-              heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+    if (DEBUG_POINTER_ASSIGNMENT) {
+        ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
+        for (size_t i = 0; i < heapSize; i++) {
+            ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
+                  heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+        }
     }
-#endif
 
     // Pull matches out by increasing order of distance.
     // To avoid reassigning pointers that have already been matched, the loop keeps track
@@ -3926,13 +3936,14 @@
                     parentIndex = childIndex;
                 }
 
-#if DEBUG_POINTER_ASSIGNMENT
-                ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
-                for (size_t j = 0; j < heapSize; j++) {
-                    ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, j,
-                          heap[j].currentPointerIndex, heap[j].lastPointerIndex, heap[j].distance);
+                if (DEBUG_POINTER_ASSIGNMENT) {
+                    ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
+                    for (size_t j = 0; j < heapSize; j++) {
+                        ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64,
+                              j, heap[j].currentPointerIndex, heap[j].lastPointerIndex,
+                              heap[j].distance);
+                    }
                 }
-#endif
             }
 
             heapSize -= 1;
@@ -3954,11 +3965,11 @@
                                                      currentPointerIndex));
             usedIdBits.markBit(id);
 
-#if DEBUG_POINTER_ASSIGNMENT
-            ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
-                  ", distance=%" PRIu64,
-                  lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
+            if (DEBUG_POINTER_ASSIGNMENT) {
+                ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
+                      ", distance=%" PRIu64,
+                      lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+            }
             break;
         }
     }
@@ -3973,9 +3984,10 @@
         current.rawPointerData.markIdBit(id,
                                          current.rawPointerData.isHovering(currentPointerIndex));
 
-#if DEBUG_POINTER_ASSIGNMENT
-        ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
-#endif
+        if (DEBUG_POINTER_ASSIGNMENT) {
+            ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex,
+                  id);
+        }
     }
 }
 
@@ -4031,63 +4043,4 @@
     return std::nullopt;
 }
 
-void TouchInputMapper::moveMouseCursor(float dx, float dy) const {
-    if (isPerWindowInputRotationEnabled()) {
-        // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate
-        // space that is oriented with the viewport.
-        rotateDelta(mViewport.orientation, &dx, &dy);
-    }
-
-    mPointerController->move(dx, dy);
-}
-
-std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const {
-    float x = 0;
-    float y = 0;
-    mPointerController->getPosition(&x, &y);
-
-    if (!isPerWindowInputRotationEnabled()) return {x, y};
-    if (!mViewport.isValid()) return {x, y};
-
-    // Convert from PointerController's rotated coordinate space that is oriented with the viewport
-    // to InputReader's un-rotated coordinate space.
-    const int32_t orientation = getInverseRotation(mViewport.orientation);
-    rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight);
-    return {x, y};
-}
-
-void TouchInputMapper::setMouseCursorPosition(float x, float y) const {
-    if (isPerWindowInputRotationEnabled() && mViewport.isValid()) {
-        // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
-        // coordinate space that is oriented with the viewport.
-        rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
-    }
-
-    mPointerController->setPosition(x, y);
-}
-
-void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
-                                     BitSet32 spotIdBits, int32_t displayId) {
-    std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
-
-    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
-        const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
-        float x = spotCoords[index].getX();
-        float y = spotCoords[index].getY();
-        float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-
-        if (isPerWindowInputRotationEnabled()) {
-            // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
-            // coordinate space.
-            rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
-        }
-
-        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
-    }
-
-    mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId);
-}
-
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index e104220..9fd30e4 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -185,6 +185,8 @@
         UNSCALED,   // unscaled mapping (touchpad)
         NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
         POINTER,    // pointer mapping (pointer)
+
+        ftl_last = POINTER
     };
     DeviceMode mDeviceMode;
 
@@ -198,6 +200,8 @@
             TOUCH_PAD,
             TOUCH_NAVIGATION,
             POINTER,
+
+            ftl_last = POINTER
         };
 
         DeviceType deviceType;
@@ -210,6 +214,8 @@
             ORIENTATION_90 = DISPLAY_ORIENTATION_90,
             ORIENTATION_180 = DISPLAY_ORIENTATION_180,
             ORIENTATION_270 = DISPLAY_ORIENTATION_270,
+
+            ftl_last = ORIENTATION_270
         };
         Orientation orientation;
 
@@ -219,6 +225,8 @@
         enum class GestureMode {
             SINGLE_TOUCH,
             MULTI_TOUCH,
+
+            ftl_last = MULTI_TOUCH
         };
         GestureMode gestureMode;
 
@@ -235,6 +243,7 @@
             DIAMETER,
             BOX,
             AREA,
+            ftl_last = AREA
         };
 
         SizeCalibration sizeCalibration;
@@ -398,8 +407,8 @@
     virtual void dumpParameters(std::string& dump);
     virtual void configureRawPointerAxes();
     virtual void dumpRawPointerAxes(std::string& dump);
-    virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
-    virtual void dumpSurface(std::string& dump);
+    virtual void configureInputDevice(nsecs_t when, bool* outResetNeeded);
+    virtual void dumpDisplay(std::string& dump);
     virtual void configureVirtualKeys();
     virtual void dumpVirtualKeys(std::string& dump);
     virtual void parseCalibration();
@@ -418,38 +427,27 @@
     // The components of the viewport are specified in the display's rotated orientation.
     DisplayViewport mViewport;
 
-    // The surface orientation, width and height set by configureSurface().
-    // The width and height are derived from the viewport but are specified
+    // The width and height are obtained from the viewport and are specified
     // in the natural orientation.
-    // They could be used for calculating diagonal, scaling factors, and virtual keys.
-    int32_t mRawSurfaceWidth;
-    int32_t mRawSurfaceHeight;
+    int32_t mDisplayWidth;
+    int32_t mDisplayHeight;
 
-    // The surface origin specifies how the surface coordinates should be translated
-    // to align with the logical display coordinate space.
-    int32_t mSurfaceLeft;
-    int32_t mSurfaceTop;
-    int32_t mSurfaceRight;
-    int32_t mSurfaceBottom;
-
-    // Similar to the surface coordinates, but in the raw display coordinate space rather than in
-    // the logical coordinate space.
+    // The physical frame is the rectangle in the display's coordinate space that maps to the
+    // the logical display frame.
     int32_t mPhysicalWidth;
     int32_t mPhysicalHeight;
     int32_t mPhysicalLeft;
     int32_t mPhysicalTop;
 
-    // The orientation may be different from the viewport orientation as it specifies
-    // the rotation of the surface coordinates required to produce the viewport's
-    // requested orientation, so it will depend on whether the device is orientation aware.
-    int32_t mSurfaceOrientation;
+    // The orientation of the input device relative to that of the display panel. It specifies
+    // the rotation of the input device coordinates required to produce the display panel
+    // orientation, so it will depend on whether the device is orientation aware.
+    int32_t mInputDeviceOrientation;
 
     // Translation and scaling factors, orientation-independent.
-    float mXTranslate;
     float mXScale;
     float mXPrecision;
 
-    float mYTranslate;
     float mYScale;
     float mYPrecision;
 
@@ -735,6 +733,10 @@
     void resetExternalStylus();
     void clearStylusDataPendingFlags();
 
+    int32_t clampResolution(const char* axisName, int32_t resolution) const;
+    void initializeOrientedRanges();
+    void initializeSizeRanges();
+
     void sync(nsecs_t when, nsecs_t readTime);
 
     bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
@@ -799,23 +801,15 @@
     // touchscreen.
     void updateTouchSpots();
 
-    bool isPointInsideSurface(int32_t x, int32_t y);
+    bool isPointInsidePhysicalFrame(int32_t x, int32_t y) const;
     const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
 
     static void assignPointerIds(const RawState& last, RawState& current);
 
     const char* modeToString(DeviceMode deviceMode);
-    void rotateAndScale(float& x, float& y);
-
-    // Wrapper methods for interfacing with PointerController. These are used to convert points
-    // between the coordinate spaces used by InputReader and PointerController, if they differ.
-    void moveMouseCursor(float dx, float dy) const;
-    std::pair<float, float> getMouseCursorPosition() const;
-    void setMouseCursorPosition(float x, float y) const;
-    void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
-                       BitSet32 spotIdBits, int32_t displayId);
+    void rotateAndScale(float& x, float& y) const;
 };
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 3df6f36..1976fed 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -41,10 +41,10 @@
 
 void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
                                   int32_t token) {
-#if DEBUG_VIBRATOR
-    ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
-          sequence.toString().c_str(), repeat, token);
-#endif
+    if (DEBUG_VIBRATOR) {
+        ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
+              sequence.toString().c_str(), repeat, token);
+    }
 
     mVibrating = true;
     mSequence = sequence;
@@ -54,14 +54,14 @@
 
     // Request InputReader to notify InputManagerService for vibration started.
     NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
-    getListener()->notifyVibratorState(&args);
+    getListener().notifyVibratorState(&args);
     nextStep();
 }
 
 void VibratorInputMapper::cancelVibrate(int32_t token) {
-#if DEBUG_VIBRATOR
-    ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
-#endif
+    if (DEBUG_VIBRATOR) {
+        ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+    }
 
     if (mVibrating && mToken == token) {
         stopVibrating();
@@ -87,9 +87,9 @@
 }
 
 void VibratorInputMapper::nextStep() {
-#if DEBUG_VIBRATOR
-    ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
-#endif
+    if (DEBUG_VIBRATOR) {
+        ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
+    }
     mIndex += 1;
     if (size_t(mIndex) >= mSequence.pattern.size()) {
         if (mRepeat < 0) {
@@ -102,16 +102,16 @@
 
     const VibrationElement& element = mSequence.pattern[mIndex];
     if (element.isOn()) {
-#if DEBUG_VIBRATOR
-        std::string description = element.toString();
-        ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
-              description.c_str());
-#endif
+        if (DEBUG_VIBRATOR) {
+            std::string description = element.toString();
+            ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+                  description.c_str());
+        }
         getDeviceContext().vibrate(element);
     } else {
-#if DEBUG_VIBRATOR
-        ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
+        if (DEBUG_VIBRATOR) {
+            ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+        }
         getDeviceContext().cancelVibrate();
     }
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -119,21 +119,21 @@
             std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
     mNextStepTime = now + duration.count();
     getContext()->requestTimeoutAtTime(mNextStepTime);
-#if DEBUG_VIBRATOR
-    ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
-#endif
+    if (DEBUG_VIBRATOR) {
+        ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
+    }
 }
 
 void VibratorInputMapper::stopVibrating() {
     mVibrating = false;
-#if DEBUG_VIBRATOR
-    ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
+    if (DEBUG_VIBRATOR) {
+        ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+    }
     getDeviceContext().cancelVibrate();
 
     // Request InputReader to notify InputManagerService for vibration complete.
     NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
-    getListener()->notifyVibratorState(&args);
+    getListener().notifyVibratorState(&args);
 }
 
 void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/sysprop/Android.bp b/services/inputflinger/sysprop/Android.bp
deleted file mode 100644
index b9d65ee..0000000
--- a/services/inputflinger/sysprop/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sysprop_library {
-    name: "InputFlingerProperties",
-    srcs: ["*.sysprop"],
-    api_packages: ["android.sysprop"],
-    property_owner: "Platform",
-}
diff --git a/services/inputflinger/sysprop/InputFlingerProperties.sysprop b/services/inputflinger/sysprop/InputFlingerProperties.sysprop
deleted file mode 100644
index 1c7e724..0000000
--- a/services/inputflinger/sysprop/InputFlingerProperties.sysprop
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2021 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the License);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an AS IS BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-module: "android.sysprop.InputFlingerProperties"
-owner: Platform
-
-# When per-window-input-rotation is enabled, InputReader works in the un-rotated
-# display coordinate space, and the display rotation is encoded as part of the
-# input window transform that is sent from SurfaceFlinger to InputDispatcher.
-prop {
-    api_name: "per_window_input_rotation"
-    type: Boolean
-    scope: Internal
-    access: ReadWrite
-    prop_name: "persist.debug.per_window_input_rotation"
-}
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 3a9994e..f13187d 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -56,18 +56,10 @@
 
 class InputClassifierTest : public testing::Test {
 protected:
-    sp<InputClassifierInterface> mClassifier;
-    sp<TestInputListener> mTestListener;
+    TestInputListener mTestListener;
+    std::unique_ptr<InputClassifierInterface> mClassifier;
 
-    virtual void SetUp() override {
-        mTestListener = new TestInputListener();
-        mClassifier = new InputClassifier(mTestListener);
-    }
-
-    virtual void TearDown() override {
-        mClassifier.clear();
-        mTestListener.clear();
-    }
+    void SetUp() override { mClassifier = std::make_unique<InputClassifier>(mTestListener); }
 };
 
 /**
@@ -80,7 +72,7 @@
 
     mClassifier->notifyConfigurationChanged(&args);
     NotifyConfigurationChangedArgs outArgs;
-    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs));
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
 }
 
@@ -93,7 +85,7 @@
 
     mClassifier->notifyKey(&args);
     NotifyKeyArgs outArgs;
-    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs));
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
 }
 
@@ -106,7 +98,7 @@
     NotifyMotionArgs motionArgs = generateBasicMotionArgs();
     mClassifier->notifyMotion(&motionArgs);
     NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(motionArgs, args);
 }
 
@@ -120,7 +112,7 @@
 
     mClassifier->notifySwitch(&args);
     NotifySwitchArgs outArgs;
-    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs));
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
 }
 
@@ -133,7 +125,7 @@
 
     mClassifier->notifyDeviceReset(&args);
     NotifyDeviceResetArgs outArgs;
-    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs));
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs));
     ASSERT_EQ(args, outArgs);
 }
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ee4d3b8..0814bc2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,12 +16,14 @@
 
 #include "../dispatcher/InputDispatcher.h"
 
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
 #include <gtest/gtest.h>
 #include <input/Input.h>
 #include <linux/input.h>
+#include <sys/epoll.h>
 
 #include <cinttypes>
 #include <thread>
@@ -73,6 +75,12 @@
     return event;
 }
 
+static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
+    ASSERT_EQ(expectedAction, receivedAction)
+            << "expected " << MotionEvent::actionToString(expectedAction) << ", got "
+            << MotionEvent::actionToString(receivedAction);
+}
+
 // --- FakeInputDispatcherPolicy ---
 
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -85,13 +93,29 @@
     FakeInputDispatcherPolicy() {}
 
     void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
-        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
-                                        args.displayId);
+        assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
+            ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_KEY);
+            EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+            const auto& keyEvent = static_cast<const KeyEvent&>(event);
+            EXPECT_EQ(keyEvent.getEventTime(), args.eventTime);
+            EXPECT_EQ(keyEvent.getAction(), args.action);
+        });
     }
 
-    void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
-        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
-                                        args.displayId);
+    void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) {
+        assertFilterInputEventWasCalledInternal([&](const InputEvent& event) {
+            ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_MOTION);
+            EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+            const auto& motionEvent = static_cast<const MotionEvent&>(event);
+            EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
+            EXPECT_EQ(motionEvent.getAction(), args.action);
+            EXPECT_EQ(motionEvent.getX(0), point.x);
+            EXPECT_EQ(motionEvent.getY(0), point.y);
+            EXPECT_EQ(motionEvent.getRawX(0), point.x);
+            EXPECT_EQ(motionEvent.getRawY(0), point.y);
+        });
     }
 
     void assertFilterInputEventWasNotCalled() {
@@ -196,21 +220,21 @@
     template <class T>
     T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
                                      std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
-        const std::chrono::time_point start = std::chrono::steady_clock::now();
-        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
-
         // If there is an ANR, Dispatcher won't be idle because there are still events
         // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
         // before checking if ANR was called.
         // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
         // to provide it some time to act. 100ms seems reasonable.
-        mNotifyAnr.wait_for(lock, timeToWait,
-                            [&storage]() REQUIRES(mLock) { return !storage.empty(); });
-        const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
-        if (storage.empty()) {
+        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+        const std::chrono::time_point start = std::chrono::steady_clock::now();
+        std::optional<T> token =
+                getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
+        if (!token.has_value()) {
             ADD_FAILURE() << "Did not receive the ANR callback";
             return {};
         }
+
+        const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
         // Ensure that the ANR didn't get raised too early. We can't be too strict here because
         // the dispatcher started counting before this function was called
         if (std::chrono::abs(timeout - waited) > 100ms) {
@@ -220,9 +244,24 @@
                           << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
                           << "ms instead";
         }
-        T token = storage.front();
+        return *token;
+    }
+
+    template <class T>
+    std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
+                                                           std::queue<T>& storage,
+                                                           std::unique_lock<std::mutex>& lock,
+                                                           std::condition_variable& condition)
+            REQUIRES(mLock) {
+        condition.wait_for(lock, timeout,
+                           [&storage]() REQUIRES(mLock) { return !storage.empty(); });
+        if (storage.empty()) {
+            ADD_FAILURE() << "Did not receive the expected callback";
+            return std::nullopt;
+        }
+        T item = storage.front();
         storage.pop();
-        return token;
+        return std::make_optional(item);
     }
 
     void assertNotifyAnrWasNotCalled() {
@@ -280,6 +319,16 @@
         mNotifyDropWindowWasCalled = false;
     }
 
+    void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) {
+        std::unique_lock lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        std::optional<sp<IBinder>> receivedToken =
+                getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
+                                                      mNotifyInputChannelBroken);
+        ASSERT_TRUE(receivedToken.has_value());
+        ASSERT_EQ(token, *receivedToken);
+    }
+
 private:
     std::mutex mLock;
     std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -298,6 +347,8 @@
     std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
     std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
     std::condition_variable mNotifyAnr;
+    std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock);
+    std::condition_variable mNotifyInputChannelBroken;
 
     sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
     bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
@@ -338,7 +389,11 @@
         mNotifyAnr.notify_all();
     }
 
-    void notifyInputChannelBroken(const sp<IBinder>&) override {}
+    void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override {
+        std::scoped_lock lock(mLock);
+        mBrokenInputChannels.push(connectionToken);
+        mNotifyInputChannelBroken.notify_all();
+    }
 
     void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
 
@@ -418,26 +473,11 @@
         mDropTargetWindowToken = token;
     }
 
-    void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
-                                         int32_t displayId) {
+    void assertFilterInputEventWasCalledInternal(
+            const std::function<void(const InputEvent&)>& verify) {
         std::scoped_lock lock(mLock);
         ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
-        ASSERT_EQ(mFilteredEvent->getType(), type);
-
-        if (type == AINPUT_EVENT_TYPE_KEY) {
-            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
-            EXPECT_EQ(keyEvent.getEventTime(), eventTime);
-            EXPECT_EQ(keyEvent.getAction(), action);
-            EXPECT_EQ(keyEvent.getDisplayId(), displayId);
-        } else if (type == AINPUT_EVENT_TYPE_MOTION) {
-            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
-            EXPECT_EQ(motionEvent.getEventTime(), eventTime);
-            EXPECT_EQ(motionEvent.getAction(), action);
-            EXPECT_EQ(motionEvent.getDisplayId(), displayId);
-        } else {
-            FAIL() << "Unknown type: " << type;
-        }
-
+        verify(*mFilteredEvent);
         mFilteredEvent = nullptr;
     }
 };
@@ -447,11 +487,11 @@
 class InputDispatcherTest : public testing::Test {
 protected:
     sp<FakeInputDispatcherPolicy> mFakePolicy;
-    sp<InputDispatcher> mDispatcher;
+    std::unique_ptr<InputDispatcher> mDispatcher;
 
     void SetUp() override {
         mFakePolicy = new FakeInputDispatcherPolicy();
-        mDispatcher = new InputDispatcher(mFakePolicy);
+        mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
         mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
         // Start InputDispatcher thread
         ASSERT_EQ(OK, mDispatcher->start());
@@ -460,7 +500,7 @@
     void TearDown() override {
         ASSERT_EQ(OK, mDispatcher->stop());
         mFakePolicy.clear();
-        mDispatcher.clear();
+        mDispatcher.reset();
     }
 
     /**
@@ -535,8 +575,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -549,8 +589,7 @@
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -562,8 +601,7 @@
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -576,8 +614,7 @@
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -589,8 +626,7 @@
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -601,8 +637,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ 0, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -612,8 +648,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -625,8 +661,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -637,8 +673,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -651,8 +687,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+                     ARBITRARY_TIME,
                      /*pointerCount*/ 2, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -683,7 +719,11 @@
 
 // --- InputDispatcherTest SetInputWindowTest ---
 static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
-static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+        android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+        android::base::HwTimeoutMultiplier());
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
@@ -796,7 +836,8 @@
             }
             case AINPUT_EVENT_TYPE_MOTION: {
                 const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
-                EXPECT_EQ(expectedAction, motionEvent.getAction());
+                assertMotionAction(expectedAction, motionEvent.getAction());
+
                 if (expectedFlags.has_value()) {
                     EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
                 }
@@ -808,6 +849,9 @@
             case AINPUT_EVENT_TYPE_CAPTURE: {
                 FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
             }
+            case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+                FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events";
+            }
             case AINPUT_EVENT_TYPE_DRAG: {
                 FAIL() << "Use 'consumeDragEvent' for DRAG events";
             }
@@ -830,7 +874,6 @@
 
         FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
         EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
-        EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
     }
 
     void consumeCaptureEvent(bool hasCapture) {
@@ -865,6 +908,20 @@
         EXPECT_EQ(y, dragEvent.getY());
     }
 
+    void consumeTouchModeEvent(bool inTouchMode) {
+        const InputEvent* event = consume();
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+                << "Got " << inputEventTypeToString(event->getType())
+                << " event instead of TOUCH_MODE event";
+
+        ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+                << mName.c_str() << ": event displayId should always be NONE.";
+        const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+        EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode());
+    }
+
     void assertNoEvents() {
         InputEvent* event = consume();
         if (event == nullptr) {
@@ -886,6 +943,10 @@
             const auto& captureEvent = static_cast<CaptureEvent&>(*event);
             ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
                           << (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
+        } else if (event->getType() == AINPUT_EVENT_TYPE_TOUCH_MODE) {
+            const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event);
+            ADD_FAILURE() << "Received touch mode event, inTouchMode = "
+                          << (touchModeEvent.isInTouchMode() ? "true" : "false");
         }
         FAIL() << mName.c_str()
                << ": should not have received any events, so consume() should return NULL";
@@ -893,6 +954,8 @@
 
     sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
 
+    int getChannelFd() { return mConsumer->getChannel()->getFd().get(); }
+
 protected:
     std::unique_ptr<InputConsumer> mConsumer;
     PreallocatedInputEventFactory mEventFactory;
@@ -906,7 +969,7 @@
     static const int32_t HEIGHT = 800;
 
     FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
-                     const sp<InputDispatcher>& dispatcher, const std::string name,
+                     const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
                      int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
           : mName(name) {
         if (token == std::nullopt) {
@@ -940,11 +1003,12 @@
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = displayId;
+        mInfo.trustedOverlay = false;
     }
 
     sp<FakeWindowHandle> clone(
             const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
-            const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+            const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId) {
         sp<FakeWindowHandle> handle =
                 new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
                                      displayId, mInfo.token);
@@ -967,21 +1031,33 @@
 
     void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
 
-    void setFrame(const Rect& frame) {
+    void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) {
         mInfo.frameLeft = frame.left;
         mInfo.frameTop = frame.top;
         mInfo.frameRight = frame.right;
         mInfo.frameBottom = frame.bottom;
-        mInfo.transform.set(-frame.left, -frame.top);
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(frame);
+
+        const Rect logicalDisplayFrame = displayTransform.transform(frame);
+        ui::Transform translate;
+        translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
+        mInfo.transform = translate * displayTransform;
     }
 
+    void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
+
+    void setType(WindowInfo::Type type) { mInfo.type = type; }
+
+    void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; }
+
     void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; }
 
     void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
 
-    void setInputFeatures(WindowInfo::Feature features) { mInfo.inputFeatures = features; }
+    void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; }
+
+    void setTrustedOverlay(bool trustedOverlay) { mInfo.trustedOverlay = trustedOverlay; }
 
     void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
         mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
@@ -1074,6 +1150,12 @@
         mInputReceiver->consumeDragEvent(isExiting, x, y);
     }
 
+    void consumeTouchModeEvent(bool inTouchMode) {
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Cannot consume events from a window with no receiver";
+        mInputReceiver->consumeTouchModeEvent(inTouchMode);
+    }
+
     std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         if (mInputReceiver == nullptr) {
             ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -1132,6 +1214,10 @@
         mInfo.ownerUid = ownerUid;
     }
 
+    void destroyReceiver() { mInputReceiver = nullptr; }
+
+    int getChannelFd() { return mInputReceiver->getChannelFd(); }
+
 private:
     const std::string mName;
     std::unique_ptr<FakeInputReceiver> mInputReceiver;
@@ -1141,7 +1227,7 @@
 std::atomic<int32_t> FakeWindowHandle::sId{1};
 
 static InputEventInjectionResult injectKey(
-        const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+        const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
         int32_t displayId = ADISPLAY_ID_NONE,
         InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1163,7 +1249,7 @@
                                         injectionTimeout, policyFlags);
 }
 
-static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher,
                                                int32_t displayId = ADISPLAY_ID_NONE) {
     return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
 }
@@ -1171,14 +1257,14 @@
 // Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
 // sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
 // has to be woken up to process the repeating key.
-static InputEventInjectionResult injectKeyDownNoRepeat(const sp<InputDispatcher>& dispatcher,
-                                                       int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDownNoRepeat(
+        const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) {
     return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId,
                      InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
                      /* allowKeyRepeat */ false);
 }
 
-static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyUp(const std::unique_ptr<InputDispatcher>& dispatcher,
                                              int32_t displayId = ADISPLAY_ID_NONE) {
     return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
 }
@@ -1280,9 +1366,8 @@
                          mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
                          mButtonState, MotionClassification::NONE, identityTransform,
                          /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
-                         mRawYCursorPosition, mDisplayOrientation, mDisplayWidth, mDisplayHeight,
-                         mEventTime, mEventTime, mPointers.size(), pointerProperties.data(),
-                         pointerCoords.data());
+                         mRawYCursorPosition, identityTransform, mEventTime, mEventTime,
+                         mPointers.size(), pointerProperties.data(), pointerCoords.data());
 
         return event;
     }
@@ -1297,15 +1382,12 @@
     int32_t mFlags{0};
     float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
     float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
-    uint32_t mDisplayOrientation{ui::Transform::ROT_0};
-    int32_t mDisplayWidth{INVALID_DISPLAY_SIZE};
-    int32_t mDisplayHeight{INVALID_DISPLAY_SIZE};
 
     std::vector<PointerBuilder> mPointers;
 };
 
 static InputEventInjectionResult injectMotionEvent(
-        const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
+        const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
         InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) {
     return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
@@ -1314,8 +1396,8 @@
 }
 
 static InputEventInjectionResult injectMotionEvent(
-        const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
-        const PointF& position,
+        const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t source,
+        int32_t displayId, const PointF& position = {100, 200},
         const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                         AMOTION_EVENT_INVALID_CURSOR_POSITION},
         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1335,13 +1417,13 @@
     return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode);
 }
 
-static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher,
-                                                  int32_t source, int32_t displayId,
-                                                  const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionDown(
+        const std::unique_ptr<InputDispatcher>& dispatcher, int32_t source, int32_t displayId,
+        const PointF& location = {100, 200}) {
     return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
 }
 
-static InputEventInjectionResult injectMotionUp(const sp<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectMotionUp(const std::unique_ptr<InputDispatcher>& dispatcher,
                                                 int32_t source, int32_t displayId,
                                                 const PointF& location = {100, 200}) {
     return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
@@ -1399,6 +1481,23 @@
     return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request);
 }
 
+/**
+ * When a window unexpectedly disposes of its input channel, policy should be notified about the
+ * broken channel.
+ */
+TEST_F(InputDispatcherTest, WhenInputChannelBreaks_PolicyIsNotified) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Window that breaks its input channel",
+                                 ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    // Window closes its channel, but the window remains.
+    window->destroyReceiver();
+    mFakePolicy->assertNotifyInputChannelBrokenWasCalled(window->getInfo()->token);
+}
+
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -1413,6 +1512,21 @@
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 }
 
+TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    // Inject a MotionEvent to an unknown display.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
 /**
  * Calling setInputWindows once with FLAG_NOT_TOUCH_MODAL should not cause any issues.
  * To ensure that window receives only events that were directly inside of it, add
@@ -1480,6 +1594,244 @@
     windowSecond->assertNoEvents();
 }
 
+/**
+ * Two windows: A top window, and a wallpaper behind the window.
+ * Touch goes to the top window, and then top window disappears. Ensure that wallpaper window
+ * gets ACTION_CANCEL.
+ * 1. foregroundWindow <-- has wallpaper (hasWallpaper=true)
+ * 2. wallpaperWindow <-- is wallpaper (type=InputWindowInfo::Type::WALLPAPER)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> foregroundWindow =
+            new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+    foregroundWindow->setHasWallpaper(true);
+    sp<FakeWindowHandle> wallpaperWindow =
+            new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    constexpr int expectedWallpaperFlags =
+            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {100, 200}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Both foreground window and its wallpaper should receive the touch down
+    foregroundWindow->consumeMotionDown();
+    wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {110, 200}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    foregroundWindow->consumeMotionMove();
+    wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    // Now the foreground window goes away, but the wallpaper stays
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+    foregroundWindow->consumeMotionCancel();
+    // Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+    wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
+/**
+ * Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above,
+ * with the following differences:
+ * After ACTION_DOWN, Wallpaper window hangs up its channel, which forces the dispatcher to
+ * clean up the connection.
+ * This later may crash dispatcher during ACTION_CANCEL synthesis, if the dispatcher is not careful.
+ * Ensure that there's no crash in the dispatcher.
+ */
+TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> foregroundWindow =
+            new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+    foregroundWindow->setHasWallpaper(true);
+    sp<FakeWindowHandle> wallpaperWindow =
+            new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    constexpr int expectedWallpaperFlags =
+            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {100, 200}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Both foreground window and its wallpaper should receive the touch down
+    foregroundWindow->consumeMotionDown();
+    wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {110, 200}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    foregroundWindow->consumeMotionMove();
+    wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    // Wallpaper closes its channel, but the window remains.
+    wallpaperWindow->destroyReceiver();
+    mFakePolicy->assertNotifyInputChannelBrokenWasCalled(wallpaperWindow->getInfo()->token);
+
+    // Now the foreground window goes away, but the wallpaper stays, even though its channel
+    // is no longer valid.
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+    foregroundWindow->consumeMotionCancel();
+}
+
+/**
+ * A single window that receives touch (on top), and a wallpaper window underneath it.
+ * The top window gets a multitouch gesture.
+ * Ensure that wallpaper gets the same gesture.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+    window->setHasWallpaper(true);
+
+    sp<FakeWindowHandle> wallpaperWindow =
+            new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    constexpr int expectedWallpaperFlags =
+            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+
+    // Touch down on top window
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {100, 100}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Both top window and its wallpaper should receive the touch down
+    window->consumeMotionDown();
+    wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    // Second finger down on the top window
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(100)
+                                     .y(100))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(150)
+                                     .y(150))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    window->consumeMotionPointerDown(1 /* pointerIndex */);
+    wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+                                              expectedWallpaperFlags);
+    window->assertNoEvents();
+    wallpaperWindow->assertNoEvents();
+}
+
+/**
+ * Two windows: a window on the left and window on the right.
+ * A third window, wallpaper, is behind both windows, and spans both top windows.
+ * The first touch down goes to the left window. A second pointer touches down on the right window.
+ * The touch is split, so both left and right windows should receive ACTION_DOWN.
+ * The wallpaper will get the full event, so it should receive ACTION_DOWN followed by
+ * ACTION_POINTER_DOWN(1).
+ */
+TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> leftWindow =
+            new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+    leftWindow->setFrame(Rect(0, 0, 200, 200));
+    leftWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+    leftWindow->setHasWallpaper(true);
+
+    sp<FakeWindowHandle> rightWindow =
+            new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+    rightWindow->setFrame(Rect(200, 0, 400, 200));
+    rightWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+    rightWindow->setHasWallpaper(true);
+
+    sp<FakeWindowHandle> wallpaperWindow =
+            new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
+    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    constexpr int expectedWallpaperFlags =
+            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+    mDispatcher->setInputWindows(
+            {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+
+    // Touch down on left window
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {100, 100}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Both foreground window and its wallpaper should receive the touch down
+    leftWindow->consumeMotionDown();
+    wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    // Second finger down on the right window
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(100)
+                                     .y(100))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(300)
+                                     .y(100))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    leftWindow->consumeMotionMove();
+    // Since the touch is split, right window gets ACTION_DOWN
+    rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+                                              expectedWallpaperFlags);
+
+    // Now, leftWindow, which received the first finger, disappears.
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}});
+    leftWindow->consumeMotionCancel();
+    // Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+    wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    // The pointer that's still down on the right window moves, and goes to the right window only.
+    // As far as the dispatcher's concerned though, both pointers are still present.
+    const MotionEvent secondFingerMoveEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(100)
+                                     .y(100))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(310)
+                                     .y(110))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT));
+    rightWindow->consumeMotionMove();
+
+    leftWindow->assertNoEvents();
+    rightWindow->assertNoEvents();
+    wallpaperWindow->assertNoEvents();
+}
+
 TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> windowLeft =
@@ -1745,8 +2097,148 @@
                          0 /*expectedFlags*/);
 }
 
-using TransferFunction =
-        std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>;
+/**
+ * Ensure the correct coordinate spaces are used by InputDispatcher.
+ *
+ * InputDispatcher works in the display space, so its coordinate system is relative to the display
+ * panel. Windows get events in the window space, and get raw coordinates in the logical display
+ * space.
+ */
+class InputDispatcherDisplayProjectionTest : public InputDispatcherTest {
+public:
+    void SetUp() override {
+        InputDispatcherTest::SetUp();
+        mDisplayInfos.clear();
+        mWindowInfos.clear();
+    }
+
+    void addDisplayInfo(int displayId, const ui::Transform& transform) {
+        gui::DisplayInfo info;
+        info.displayId = displayId;
+        info.transform = transform;
+        mDisplayInfos.push_back(std::move(info));
+        mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+    }
+
+    void addWindow(const sp<WindowInfoHandle>& windowHandle) {
+        mWindowInfos.push_back(*windowHandle->getInfo());
+        mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+    }
+
+    // Set up a test scenario where the display has a scaled projection and there are two windows
+    // on the display.
+    std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() {
+        // The display has a projection that has a scale factor of 2 and 4 in the x and y directions
+        // respectively.
+        ui::Transform displayTransform;
+        displayTransform.set(2, 0, 0, 4);
+        addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
+
+        // Add two windows to the display. Their frames are represented in the display space.
+        sp<FakeWindowHandle> firstWindow =
+                new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+        firstWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+        firstWindow->setFrame(Rect(0, 0, 100, 200), displayTransform);
+        addWindow(firstWindow);
+
+        sp<FakeWindowHandle> secondWindow =
+                new FakeWindowHandle(application, mDispatcher, "Second Window",
+                                     ADISPLAY_ID_DEFAULT);
+        secondWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+        secondWindow->setFrame(Rect(100, 200, 200, 400), displayTransform);
+        addWindow(secondWindow);
+        return {std::move(firstWindow), std::move(secondWindow)};
+    }
+
+private:
+    std::vector<gui::DisplayInfo> mDisplayInfos;
+    std::vector<gui::WindowInfo> mWindowInfos;
+};
+
+TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+    // Send down to the first window. The point is represented in the display space. The point is
+    // selected so that if the hit test was done with the transform applied to it, then it would
+    // end up in the incorrect window.
+    NotifyMotionArgs downMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
+    mDispatcher->notifyMotion(&downMotionArgs);
+
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+}
+
+// Ensure that when a MotionEvent is injected through the InputDispatcher::injectInputEvent() API,
+// the event should be treated as being in the logical display space.
+TEST_F(InputDispatcherDisplayProjectionTest, InjectionInLogicalDisplaySpace) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+    // Send down to the first window. The point is represented in the logical display space. The
+    // point is selected so that if the hit test was done in logical display space, then it would
+    // end up in the incorrect window.
+    injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                     PointF{75 * 2, 55 * 4});
+
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+}
+
+// Ensure that when a MotionEvent that has a custom transform is injected, the post-transformed
+// event should be treated as being in the logical display space.
+TEST_F(InputDispatcherDisplayProjectionTest, InjectionWithTransformInLogicalDisplaySpace) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+    const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+    ui::Transform injectedEventTransform;
+    injectedEventTransform.set(matrix);
+    const vec2 expectedPoint{75, 55}; // The injected point in the logical display space.
+    const vec2 untransformedPoint = injectedEventTransform.inverse().transform(expectedPoint);
+
+    MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                .displayId(ADISPLAY_ID_DEFAULT)
+                                .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                                .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                                 .x(untransformedPoint.x)
+                                                 .y(untransformedPoint.y))
+                                .build();
+    event.transform(matrix);
+
+    injectMotionEvent(mDispatcher, event, INJECT_EVENT_TIMEOUT,
+                      InputEventInjectionSync::WAIT_FOR_RESULT);
+
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinateSpace) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+    // Send down to the second window.
+    NotifyMotionArgs downMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {PointF{150, 220}});
+    mDispatcher->notifyMotion(&downMotionArgs);
+
+    firstWindow->assertNoEvents();
+    const MotionEvent* event = secondWindow->consumeMotion();
+    EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+
+    // Ensure that the events from the "getRaw" API are in logical display coordinates.
+    EXPECT_EQ(300, event->getRawX(0));
+    EXPECT_EQ(880, event->getRawY(0));
+
+    // Ensure that the x and y values are in the window's coordinate space.
+    // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
+    // the logical display space. This will be the origin of the window space.
+    EXPECT_EQ(100, event->getX(0));
+    EXPECT_EQ(80, event->getY(0));
+}
+
+using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
+                                            sp<IBinder>, sp<IBinder>)>;
 
 class TransferTouchFixture : public InputDispatcherTest,
                              public ::testing::WithParamInterface<TransferFunction> {};
@@ -1859,12 +2351,12 @@
 // for the case where there are multiple pointers split across several windows.
 INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
                          ::testing::Values(
-                                 [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/,
-                                     sp<IBinder> destChannelToken) {
+                                 [&](const std::unique_ptr<InputDispatcher>& dispatcher,
+                                     sp<IBinder> /*ignored*/, sp<IBinder> destChannelToken) {
                                      return dispatcher->transferTouch(destChannelToken);
                                  },
-                                 [&](sp<InputDispatcher> dispatcher, sp<IBinder> from,
-                                     sp<IBinder> to) {
+                                 [&](const std::unique_ptr<InputDispatcher>& dispatcher,
+                                     sp<IBinder> from, sp<IBinder> to) {
                                      return dispatcher->transferTouchFocus(from, to,
                                                                            false /*isDragAndDrop*/);
                                  }));
@@ -2273,7 +2765,7 @@
 
 class FakeMonitorReceiver {
 public:
-    FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+    FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
                         int32_t displayId, bool isGestureMonitor = false) {
         base::Result<std::unique_ptr<InputChannel>> channel =
                 dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID);
@@ -2296,6 +2788,11 @@
                                      expectedDisplayId, expectedFlags);
     }
 
+    void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE,
+                                     expectedDisplayId, expectedFlags);
+    }
+
     void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
         mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
                                      expectedDisplayId, expectedFlags);
@@ -2332,6 +2829,57 @@
     std::unique_ptr<FakeInputReceiver> mInputReceiver;
 };
 
+/**
+ * Two entities that receive touch: A window, and a global monitor.
+ * The touch goes to the window, and then the window disappears.
+ * The monitor does not get cancel right away. But if more events come in, the touch gets canceled
+ * for the monitor, as well.
+ * 1. foregroundWindow
+ * 2. monitor <-- global monitor (doesn't observe z order, receives all events)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT,
+                                false /*isGestureMonitor*/);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {100, 200}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Both the foreground window and the global monitor should receive the touch down
+    window->consumeMotionDown();
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {110, 200}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    window->consumeMotionMove();
+    monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+
+    // Now the foreground window goes away
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+    window->consumeMotionCancel();
+    monitor.assertNoEvents(); // Global monitor does not get a cancel yet
+
+    // If more events come in, there will be no more foreground window to send them to. This will
+    // cause a cancel for the monitor, as well.
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {120, 200}))
+            << "Injection should fail because the window was removed";
+    window->assertNoEvents();
+    // Global monitor now gets the cancel
+    monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+}
+
 // Tests for gesture monitors
 TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -2433,6 +2981,17 @@
     ASSERT_EQ(ui::Transform(), event->getTransform());
 }
 
+TEST_F(InputDispatcherTest, GestureMonitor_NoWindow) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
 TEST_F(InputDispatcherTest, TestMoveEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -2561,7 +3120,6 @@
     SCOPED_TRACE("Check default value of touch mode");
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     setFocusedWindow(window);
-
     window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
 
     SCOPED_TRACE("Remove the window to trigger focus loss");
@@ -2571,6 +3129,7 @@
 
     SCOPED_TRACE("Disable touch mode");
     mDispatcher->setInTouchMode(false);
+    window->consumeTouchModeEvent(false);
     window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     setFocusedWindow(window);
@@ -2583,6 +3142,7 @@
 
     SCOPED_TRACE("Enable touch mode again");
     mDispatcher->setInTouchMode(true);
+    window->consumeTouchModeEvent(true);
     window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     setFocusedWindow(window);
@@ -2622,8 +3182,8 @@
     const VerifiedKeyEvent& verifiedKey = static_cast<const VerifiedKeyEvent&>(*verified);
 
     ASSERT_EQ(keyArgs.action, verifiedKey.action);
-    ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
     ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags);
+    ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
     ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode);
     ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
     ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState);
@@ -2637,7 +3197,14 @@
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    ui::Transform transform;
+    transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
+
+    gui::DisplayInfo displayInfo;
+    displayInfo.displayId = ADISPLAY_ID_DEFAULT;
+    displayInfo.transform = transform;
+
+    mDispatcher->onWindowInfosChanged({*window->getInfo()}, {displayInfo});
 
     NotifyMotionArgs motionArgs =
             generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -2658,68 +3225,18 @@
 
     const VerifiedMotionEvent& verifiedMotion = static_cast<const VerifiedMotionEvent&>(*verified);
 
-    EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX);
-    EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY);
+    const vec2 rawXY =
+            MotionEvent::calculateTransformedXY(motionArgs.source, transform,
+                                                motionArgs.pointerCoords[0].getXYValue());
+    EXPECT_EQ(rawXY.x, verifiedMotion.rawX);
+    EXPECT_EQ(rawXY.y, verifiedMotion.rawY);
     EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked);
-    EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
     EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags);
+    EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
     EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
     EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
 }
 
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) {
-    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> window =
-            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
-    const std::string name = window->getName();
-
-    // Window gets transformed by offset values.
-    window->setWindowOffset(500.0f, 500.0f);
-
-    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-    window->setFocusable(true);
-
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-
-    // First, we set focused window so that focusedWindowHandle is not null.
-    setFocusedWindow(window);
-
-    // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
-    window->consumeFocusEvent(true);
-
-    constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
-                                                                  AMOTION_EVENT_ACTION_DOWN),
-                                                        std::pair(AINPUT_SOURCE_JOYSTICK,
-                                                                  AMOTION_EVENT_ACTION_MOVE)};
-    for (const auto& [source, action] : nonTransformedSources) {
-        const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT);
-        mDispatcher->notifyMotion(&motionArgs);
-
-        MotionEvent* event = window->consumeMotion();
-        ASSERT_NE(event, nullptr);
-
-        const MotionEvent& motionEvent = *event;
-        EXPECT_EQ(action, motionEvent.getAction());
-        EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
-
-        float expectedX = motionArgs.pointerCoords[0].getX();
-        float expectedY = motionArgs.pointerCoords[0].getY();
-
-        // Ensure the axis values from the final motion event are not transformed.
-        EXPECT_EQ(expectedX, motionEvent.getX(0))
-                << "expected " << expectedX << " for x coord of " << name.c_str() << ", got "
-                << motionEvent.getX(0);
-        EXPECT_EQ(expectedY, motionEvent.getY(0))
-                << "expected " << expectedY << " for y coord of " << name.c_str() << ", got "
-                << motionEvent.getY(0);
-        // Ensure the raw and transformed axis values for the motion event are the same.
-        EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0))
-                << "expected raw and transformed X-axis values to be equal";
-        EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0))
-                << "expected raw and transformed Y-axis values to be equal";
-    }
-}
-
 /**
  * Ensure that separate calls to sign the same data are generating the same key.
  * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
@@ -3015,7 +3532,7 @@
     virtual void SetUp() override {
         mFakePolicy = new FakeInputDispatcherPolicy();
         mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
-        mDispatcher = new InputDispatcher(mFakePolicy);
+        mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
         mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
         ASSERT_EQ(OK, mDispatcher->start());
 
@@ -3316,9 +3833,53 @@
     secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 }
 
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
+    FakeMonitorReceiver monitorInPrimary =
+            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitorInSecondary =
+            FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+    // Test touch down on primary display.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // Test touch down on second display.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+    monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
+
+    // Trigger cancel touch.
+    mDispatcher->cancelCurrentTouch();
+    windowInPrimary->consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+    monitorInPrimary.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+    windowInSecondary->consumeMotionCancel(SECOND_DISPLAY_ID);
+    monitorInSecondary.consumeMotionCancel(SECOND_DISPLAY_ID);
+
+    // Test inject a move motion event, no window/monitor should receive the event.
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {110, 200}))
+            << "Inject motion event should return InputEventInjectionResult::FAILED";
+    windowInPrimary->assertNoEvents();
+    monitorInPrimary.assertNoEvents();
+
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                SECOND_DISPLAY_ID, {110, 200}))
+            << "Inject motion event should return InputEventInjectionResult::FAILED";
+    windowInSecondary->assertNoEvents();
+    monitorInSecondary.assertNoEvents();
+}
+
 class InputFilterTest : public InputDispatcherTest {
 protected:
-    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered,
+                          const ui::Transform& transform = ui::Transform()) {
         NotifyMotionArgs motionArgs;
 
         motionArgs =
@@ -3329,7 +3890,8 @@
         mDispatcher->notifyMotion(&motionArgs);
         ASSERT_TRUE(mDispatcher->waitForIdle());
         if (expectToBeFiltered) {
-            mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
+            const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
+            mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy);
         } else {
             mFakePolicy->assertFilterInputEventWasNotCalled();
         }
@@ -3387,6 +3949,30 @@
     testNotifyKey(/*expectToBeFiltered*/ false);
 }
 
+// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
+// logical display coordinate space.
+TEST_F(InputFilterTest, MotionEvent_UsesLogicalDisplayCoordinates_notifyMotion) {
+    ui::Transform firstDisplayTransform;
+    firstDisplayTransform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
+    ui::Transform secondDisplayTransform;
+    secondDisplayTransform.set({-6.6, -5.5, -4.4, -3.3, -2.2, -1.1, 0, 0, 1});
+
+    std::vector<gui::DisplayInfo> displayInfos(2);
+    displayInfos[0].displayId = ADISPLAY_ID_DEFAULT;
+    displayInfos[0].transform = firstDisplayTransform;
+    displayInfos[1].displayId = SECOND_DISPLAY_ID;
+    displayInfos[1].transform = secondDisplayTransform;
+
+    mDispatcher->onWindowInfosChanged({}, displayInfos);
+
+    // Enable InputFilter
+    mDispatcher->setInputFilterEnabled(true);
+
+    // Ensure the correct transforms are used for the displays.
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
+}
+
 class InputFilterInjectionPolicyTest : public InputDispatcherTest {
 protected:
     virtual void SetUp() override {
@@ -3451,8 +4037,7 @@
                          DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE,
                          identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
-                         0 /*INVALID_DISPLAY_SIZE*/, 0 /*INVALID_DISPLAY_SIZE*/, eventTime,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime,
                          eventTime,
                          /*pointerCount*/ 1, pointerProperties, pointerCoords);
 
@@ -3659,7 +4244,7 @@
                 << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
         const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
-        EXPECT_EQ(expectedAction, motionEvent.getAction());
+        assertMotionAction(expectedAction, motionEvent.getAction());
 
         for (size_t i = 0; i < points.size(); i++) {
             float expectedX = points[i].x;
@@ -3674,7 +4259,7 @@
         }
     }
 
-    void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints,
+    void touchAndAssertPositions(int32_t action, const std::vector<PointF>& touchedPoints,
                                  std::vector<PointF> expectedPoints) {
         NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
                                                          ADISPLAY_ID_DEFAULT, touchedPoints);
@@ -5693,4 +6278,438 @@
     window->assertNoEvents();
 }
 
+class InputDispatcherTouchModeChangedTests : public InputDispatcherTest {
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+    sp<FakeWindowHandle> mSecondWindow;
+
+    void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApp = std::make_shared<FakeApplicationHandle>();
+        mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mWindow->setFocusable(true);
+        mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+        mSecondWindow->setFocusable(true);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+
+        setFocusedWindow(mWindow);
+        mWindow->consumeFocusEvent(true);
+    }
+
+    void changeAndVerifyTouchMode(bool inTouchMode) {
+        mDispatcher->setInTouchMode(inTouchMode);
+        mWindow->consumeTouchModeEvent(inTouchMode);
+        mSecondWindow->consumeTouchModeEvent(inTouchMode);
+    }
+};
+
+TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchModeOnFocusedWindow) {
+    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode);
+}
+
+TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
+    mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
+class InputDispatcherSpyWindowTest : public InputDispatcherTest {
+public:
+    sp<FakeWindowHandle> createSpy(const Flags<WindowInfo::Flag> flags) {
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
+        std::string name = "Fake Spy ";
+        name += std::to_string(mSpyCount++);
+        sp<FakeWindowHandle> spy =
+                new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT);
+        spy->setInputFeatures(WindowInfo::Feature::SPY);
+        spy->setTrustedOverlay(true);
+        spy->addFlags(flags);
+        return spy;
+    }
+
+    sp<FakeWindowHandle> createForeground() {
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> window =
+                new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+        window->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
+        return window;
+    }
+
+private:
+    int mSpyCount{0};
+};
+
+/**
+ * Adding a spy window that is not a trusted overlay causes Dispatcher to abort.
+ */
+TEST_F(InputDispatcherSpyWindowTest, UntrustedSpy_AbortsDispatcher) {
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    spy->setTrustedOverlay(false);
+    ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}),
+                 ".* not a trusted overlay");
+}
+
+/**
+ * Input injection into a display with a spy window but no foreground windows should succeed.
+ */
+TEST_F(InputDispatcherSpyWindowTest, NoForegroundWindow) {
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
+/**
+ * Verify the order in which different input windows receive events. The touched foreground window
+ * (if there is one) should always receive the event first. When there are multiple spy windows, the
+ * spy windows will receive the event according to their Z-order, where the top-most spy window will
+ * receive events before ones belows it.
+ *
+ * Here, we set up a scenario with four windows in the following Z order from the top:
+ *    spy1, spy2, window, spy3.
+ * We then inject an event and verify that the foreground "window" receives it first, followed by
+ * "spy1" and "spy2". The "spy3" does not receive the event because it is underneath the foreground
+ * window.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ReceivesInputInOrder) {
+    auto window = createForeground();
+    auto spy1 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy2 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy3 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window, spy3}}});
+    const std::vector<sp<FakeWindowHandle>> channels{spy1, spy2, window, spy3};
+    const size_t numChannels = channels.size();
+
+    base::unique_fd epollFd(epoll_create1(0 /*flags*/));
+    if (!epollFd.ok()) {
+        FAIL() << "Failed to create epoll fd";
+    }
+
+    for (size_t i = 0; i < numChannels; i++) {
+        struct epoll_event event = {.events = EPOLLIN, .data.u64 = i};
+        if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, channels[i]->getChannelFd(), &event) < 0) {
+            FAIL() << "Failed to add fd to epoll";
+        }
+    }
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    std::vector<size_t> eventOrder;
+    std::vector<struct epoll_event> events(numChannels);
+    for (;;) {
+        const int nFds = epoll_wait(epollFd.get(), events.data(), static_cast<int>(numChannels),
+                                    (100ms).count());
+        if (nFds < 0) {
+            FAIL() << "Failed to call epoll_wait";
+        }
+        if (nFds == 0) {
+            break; // epoll_wait timed out
+        }
+        for (int i = 0; i < nFds; i++) {
+            ASSERT_EQ(EPOLLIN, events[i].events);
+            eventOrder.push_back(events[i].data.u64);
+            channels[i]->consumeMotionDown();
+        }
+    }
+
+    // Verify the order in which the events were received.
+    EXPECT_EQ(3u, eventOrder.size());
+    EXPECT_EQ(2u, eventOrder[0]); // index 2: window
+    EXPECT_EQ(0u, eventOrder[1]); // index 0: spy1
+    EXPECT_EQ(1u, eventOrder[2]); // index 1: spy2
+}
+
+/**
+ * A spy window using the NOT_TOUCHABLE flag does not receive events.
+ */
+TEST_F(InputDispatcherSpyWindowTest, NotTouchable) {
+    auto window = createForeground();
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCHABLE);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    spy->assertNoEvents();
+}
+
+/**
+ * A spy window will only receive gestures that originate within its touchable region. Gestures that
+ * have their ACTION_DOWN outside of the touchable region of the spy window will not be dispatched
+ * to the window.
+ */
+TEST_F(InputDispatcherSpyWindowTest, TouchableRegion) {
+    auto window = createForeground();
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    spy->setTouchableRegion(Region{{0, 0, 20, 20}});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+    // Inject an event outside the spy window's touchable region.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown();
+    spy->assertNoEvents();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionUp();
+    spy->assertNoEvents();
+
+    // Inject an event inside the spy window's touchable region.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {5, 10}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown();
+    spy->consumeMotionDown();
+}
+
+/**
+ * A spy window that is a modal window will receive gestures outside of its frame and touchable
+ * region.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ModalWindow) {
+    auto window = createForeground();
+    auto spy = createSpy(static_cast<WindowInfo::Flag>(0));
+    // This spy window does not have the NOT_TOUCH_MODAL flag set.
+    spy->setFrame(Rect{0, 0, 20, 20});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+    // Inject an event outside the spy window's frame and touchable region.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown();
+    spy->consumeMotionDown();
+}
+
+/**
+ * A spy window can listen for touches outside its touchable region using the WATCH_OUTSIDE_TOUCHES
+ * flag.
+ */
+TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) {
+    auto window = createForeground();
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+    spy->setFrame(Rect{0, 0, 20, 20});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+    // Inject an event outside the spy window's frame and touchable region.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown();
+    spy->consumeMotionOutside();
+}
+
+/**
+ * A spy window can pilfer pointers. When this happens, touch gestures that are currently sent to
+ * any other windows - including other spy windows - will also be cancelled.
+ */
+TEST_F(InputDispatcherSpyWindowTest, PilferPointers) {
+    auto window = createForeground();
+    auto spy1 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy2 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown();
+    spy1->consumeMotionDown();
+    spy2->consumeMotionDown();
+
+    // Pilfer pointers from the second spy window.
+    mDispatcher->pilferPointers(spy2->getToken());
+    spy2->assertNoEvents();
+    spy1->consumeMotionCancel();
+    window->consumeMotionCancel();
+
+    // The rest of the gesture should only be sent to the second spy window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    spy2->consumeMotionMove();
+    spy1->assertNoEvents();
+    window->assertNoEvents();
+}
+
+/**
+ * Even when a spy window spans over multiple foreground windows, the spy should receive all
+ * pointers that are down within its bounds.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ReceivesMultiplePointers) {
+    auto windowLeft = createForeground();
+    windowLeft->setFrame({0, 0, 100, 200});
+    auto windowRight = createForeground();
+    windowRight->setFrame({100, 0, 200, 200});
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    spy->setFrame({0, 0, 200, 200});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, windowLeft, windowRight}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    windowLeft->consumeMotionDown();
+    spy->consumeMotionDown();
+
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(
+                            PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    windowRight->consumeMotionDown();
+    spy->consumeMotionPointerDown(1 /*pointerIndex*/);
+}
+
+/**
+ * When the first pointer lands outside the spy window and the second pointer lands inside it, the
+ * the spy should receive the second pointer with ACTION_DOWN.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) {
+    auto window = createForeground();
+    window->setFrame({0, 0, 200, 200});
+    auto spyRight = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    spyRight->setFrame({100, 0, 200, 200});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyRight, window}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown();
+    spyRight->assertNoEvents();
+
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(
+                            PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionPointerDown(1 /*pointerIndex*/);
+    spyRight->consumeMotionDown();
+}
+
+class InputDispatcherStylusInterceptorTest : public InputDispatcherTest {
+public:
+    std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() {
+        std::shared_ptr<FakeApplicationHandle> overlayApplication =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> overlay =
+                new FakeWindowHandle(overlayApplication, mDispatcher, "Stylus interceptor window",
+                                     ADISPLAY_ID_DEFAULT);
+        overlay->setFocusable(false);
+        overlay->setOwnerInfo(111, 111);
+        overlay->setFlags(WindowInfo::Flag::NOT_TOUCHABLE | WindowInfo::Flag::SPLIT_TOUCH);
+        overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS);
+        overlay->setTrustedOverlay(true);
+
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> window =
+                new FakeWindowHandle(application, mDispatcher, "Application window",
+                                     ADISPLAY_ID_DEFAULT);
+        window->setFocusable(true);
+        window->setOwnerInfo(222, 222);
+        window->setFlags(WindowInfo::Flag::SPLIT_TOUCH);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+        setFocusedWindow(window);
+        window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+        return {std::move(overlay), std::move(window)};
+    }
+
+    void sendFingerEvent(int32_t action) {
+        NotifyMotionArgs motionArgs =
+                generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+                                   ADISPLAY_ID_DEFAULT, {PointF{20, 20}});
+        mDispatcher->notifyMotion(&motionArgs);
+    }
+
+    void sendStylusEvent(int32_t action) {
+        NotifyMotionArgs motionArgs =
+                generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+                                   ADISPLAY_ID_DEFAULT, {PointF{30, 40}});
+        motionArgs.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+        mDispatcher->notifyMotion(&motionArgs);
+    }
+};
+
+TEST_F(InputDispatcherStylusInterceptorTest, UntrustedOverlay_AbortsDispatcher) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    overlay->setTrustedOverlay(false);
+    // Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort.
+    ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}),
+                 ".* not a trusted overlay");
+}
+
+TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
+    overlay->consumeMotionDown();
+    sendStylusEvent(AMOTION_EVENT_ACTION_UP);
+    overlay->consumeMotionUp();
+
+    sendFingerEvent(AMOTION_EVENT_ACTION_DOWN);
+    window->consumeMotionDown();
+    sendFingerEvent(AMOTION_EVENT_ACTION_UP);
+    window->consumeMotionUp();
+
+    overlay->assertNoEvents();
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
+    overlay->consumeMotionDown();
+    window->consumeMotionDown();
+    sendStylusEvent(AMOTION_EVENT_ACTION_UP);
+    overlay->consumeMotionUp();
+    window->consumeMotionUp();
+
+    sendFingerEvent(AMOTION_EVENT_ACTION_DOWN);
+    window->consumeMotionDown();
+    sendFingerEvent(AMOTION_EVENT_ACTION_UP);
+    window->consumeMotionUp();
+
+    overlay->assertNoEvents();
+    window->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index b419d9a..2c5b321 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,7 +16,6 @@
 
 #include <CursorInputMapper.h>
 #include <InputDevice.h>
-#include <InputFlingerProperties.sysprop.h>
 #include <InputMapper.h>
 #include <InputReader.h>
 #include <InputReaderBase.h>
@@ -79,6 +78,15 @@
 static constexpr int32_t LIGHT_COLOR = 0x7F448866;
 static constexpr int32_t LIGHT_PLAYER_ID = 2;
 
+static constexpr int32_t ACTION_POINTER_0_DOWN =
+        AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t ACTION_POINTER_0_UP =
+        AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t ACTION_POINTER_1_DOWN =
+        AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t ACTION_POINTER_1_UP =
+        AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
 
@@ -107,6 +115,24 @@
     }
 }
 
+static void assertAxisResolution(MultiTouchInputMapper& mapper, int axis, float resolution) {
+    InputDeviceInfo info;
+    mapper.populateDeviceInfo(&info);
+
+    const InputDeviceInfo::MotionRange* motionRange =
+            info.getMotionRange(axis, AINPUT_SOURCE_TOUCHSCREEN);
+    ASSERT_NEAR(motionRange->resolution, resolution, EPSILON);
+}
+
+static void assertAxisNotPresent(MultiTouchInputMapper& mapper, int axis) {
+    InputDeviceInfo info;
+    mapper.populateDeviceInfo(&info);
+
+    const InputDeviceInfo::MotionRange* motionRange =
+            info.getMotionRange(axis, AINPUT_SOURCE_TOUCHSCREEN);
+    ASSERT_EQ(nullptr, motionRange);
+}
+
 // --- FakePointerController ---
 
 class FakePointerController : public PointerControllerInterface {
@@ -205,7 +231,7 @@
     std::condition_variable mDevicesChangedCondition;
 
     InputReaderConfiguration mConfig;
-    std::unordered_map<int32_t, std::shared_ptr<FakePointerController>> mPointerControllers;
+    std::shared_ptr<FakePointerController> mPointerController;
     std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
     bool mInputDevicesChanged GUARDED_BY(mLock){false};
     std::vector<DisplayViewport> mViewports;
@@ -250,14 +276,35 @@
         return mConfig.getDisplayViewportByPort(displayPort);
     }
 
+    void addDisplayViewport(DisplayViewport viewport) {
+        mViewports.push_back(std::move(viewport));
+        mConfig.setDisplayViewports(mViewports);
+    }
+
     void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
                             bool isActive, const std::string& uniqueId,
-                            std::optional<uint8_t> physicalPort, ViewportType viewportType) {
-        const DisplayViewport viewport =
-                createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId,
-                                      physicalPort, viewportType);
-        mViewports.push_back(viewport);
-        mConfig.setDisplayViewports(mViewports);
+                            std::optional<uint8_t> physicalPort, ViewportType type) {
+        const bool isRotated =
+                (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
+        DisplayViewport v;
+        v.displayId = displayId;
+        v.orientation = orientation;
+        v.logicalLeft = 0;
+        v.logicalTop = 0;
+        v.logicalRight = isRotated ? height : width;
+        v.logicalBottom = isRotated ? width : height;
+        v.physicalLeft = 0;
+        v.physicalTop = 0;
+        v.physicalRight = isRotated ? height : width;
+        v.physicalBottom = isRotated ? width : height;
+        v.deviceWidth = isRotated ? height : width;
+        v.deviceHeight = isRotated ? width : height;
+        v.isActive = isActive;
+        v.uniqueId = uniqueId;
+        v.physicalPort = physicalPort;
+        v.type = type;
+
+        addDisplayViewport(v);
     }
 
     bool updateViewport(const DisplayViewport& viewport) {
@@ -291,8 +338,8 @@
 
     void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
 
-    void setPointerController(int32_t deviceId, std::shared_ptr<FakePointerController> controller) {
-        mPointerControllers.insert_or_assign(deviceId, std::move(controller));
+    void setPointerController(std::shared_ptr<FakePointerController> controller) {
+        mPointerController = std::move(controller);
     }
 
     const InputReaderConfiguration* getReaderConfiguration() const {
@@ -330,38 +377,13 @@
 private:
     uint32_t mNextPointerCaptureSequenceNumber = 0;
 
-    DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
-                                          int32_t orientation, bool isActive,
-                                          const std::string& uniqueId,
-                                          std::optional<uint8_t> physicalPort, ViewportType type) {
-        bool isRotated = (orientation == DISPLAY_ORIENTATION_90
-                || orientation == DISPLAY_ORIENTATION_270);
-        DisplayViewport v;
-        v.displayId = displayId;
-        v.orientation = orientation;
-        v.logicalLeft = 0;
-        v.logicalTop = 0;
-        v.logicalRight = isRotated ? height : width;
-        v.logicalBottom = isRotated ? width : height;
-        v.physicalLeft = 0;
-        v.physicalTop = 0;
-        v.physicalRight = isRotated ? height : width;
-        v.physicalBottom = isRotated ? width : height;
-        v.deviceWidth = isRotated ? height : width;
-        v.deviceHeight = isRotated ? width : height;
-        v.isActive = isActive;
-        v.uniqueId = uniqueId;
-        v.physicalPort = physicalPort;
-        v.type = type;
-        return v;
-    }
-
     void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
         *outConfig = mConfig;
     }
 
-    std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
-        return mPointerControllers[deviceId];
+    std::shared_ptr<PointerControllerInterface> obtainPointerController(
+            int32_t /*deviceId*/) override {
+        return mPointerController;
     }
 
     void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
@@ -873,6 +895,24 @@
         return false;
     }
 
+    bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override {
+        Device* device = getDevice(deviceId);
+        if (!device) {
+            return false;
+        }
+        for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
+            if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
+                return true;
+            }
+        }
+        for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+            if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     bool hasLed(int32_t deviceId, int32_t led) const override {
         Device* device = getDevice(deviceId);
         return device && device->leds.indexOfKey(led) >= 0;
@@ -1172,7 +1212,7 @@
 public:
     InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
                             const sp<InputReaderPolicyInterface>& policy,
-                            const sp<InputListenerInterface>& listener)
+                            InputListenerInterface& listener)
           : InputReader(eventHub, policy, listener), mFakeContext(this) {}
 
     virtual ~InstrumentedInputReader() {}
@@ -1485,7 +1525,7 @@
 
 class InputReaderTest : public testing::Test {
 protected:
-    sp<TestInputListener> mFakeListener;
+    std::unique_ptr<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     std::unique_ptr<InstrumentedInputReader> mReader;
@@ -1493,14 +1533,14 @@
     void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new TestInputListener();
+        mFakeListener = std::make_unique<TestInputListener>();
 
         mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
-                                                            mFakeListener);
+                                                            *mFakeListener);
     }
 
     void TearDown() override {
-        mFakeListener.clear();
+        mFakeListener.reset();
         mFakePolicy.clear();
     }
 
@@ -2131,16 +2171,21 @@
 // the tests to fail.
 class InputReaderIntegrationTest : public testing::Test {
 protected:
-    sp<TestInputListener> mTestListener;
+    std::unique_ptr<TestInputListener> mTestListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<InputReaderInterface> mReader;
+    std::unique_ptr<InputReaderInterface> mReader;
+
+    std::shared_ptr<FakePointerController> mFakePointerController;
 
     void SetUp() override {
         mFakePolicy = new FakeInputReaderPolicy();
-        mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/,
-                                              30ms /*eventDidNotHappenTimeout*/);
+        mFakePointerController = std::make_shared<FakePointerController>();
+        mFakePolicy->setPointerController(mFakePointerController);
+        mTestListener = std::make_unique<TestInputListener>(2000ms /*eventHappenedTimeout*/,
+                                                            30ms /*eventDidNotHappenTimeout*/);
 
-        mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
+        mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
+                                                *mTestListener);
         ASSERT_EQ(mReader->start(), OK);
 
         // Since this test is run on a real device, all the input devices connected
@@ -2152,7 +2197,8 @@
 
     void TearDown() override {
         ASSERT_EQ(mReader->stop(), OK);
-        mTestListener.clear();
+        mReader.reset();
+        mTestListener.reset();
         mFakePolicy.clear();
     }
 };
@@ -2284,6 +2330,17 @@
         mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
+    void assertReceivedMotion(int32_t action, const std::vector<Point>& points) {
+        NotifyMotionArgs args;
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+        EXPECT_EQ(action, args.action);
+        ASSERT_EQ(points.size(), args.pointerCount);
+        for (size_t i = 0; i < args.pointerCount; i++) {
+            EXPECT_EQ(points[i].x, args.pointerCoords[i].getX());
+            EXPECT_EQ(points[i].y, args.pointerCoords[i].getY());
+        }
+    }
+
     std::unique_ptr<UinputTouchScreen> mDevice;
 };
 
@@ -2294,16 +2351,19 @@
     // ACTION_DOWN
     mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
     // ACTION_MOVE
     mDevice->sendMove(centerPoint + Point(1, 1));
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // ACTION_UP
     mDevice->sendUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
@@ -2316,6 +2376,7 @@
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
@@ -2323,29 +2384,129 @@
     const Point secondPoint = centerPoint + Point(100, 100);
     mDevice->sendSlot(SECOND_SLOT);
     mDevice->sendTrackingId(SECOND_TRACKING_ID);
-    mDevice->sendDown(secondPoint + Point(1, 1));
+    mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              args.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
 
     // ACTION_MOVE (Second slot)
-    mDevice->sendMove(secondPoint);
+    mDevice->sendMove(secondPoint + Point(1, 1));
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // ACTION_POINTER_UP (Second slot)
     mDevice->sendPointerUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              args.action);
+    ASSERT_EQ(ACTION_POINTER_1_UP, args.action);
 
     // ACTION_UP
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
+/**
+ * What happens when a pointer goes up while another pointer moves in the same frame? Are POINTER_UP
+ * events guaranteed to contain the same data as a preceding MOVE, or can they contain different
+ * data?
+ * In this test, we try to send a change in coordinates in Pointer 0 in the same frame as the
+ * liftoff of Pointer 1. We check that POINTER_UP event is generated first, and the MOVE event
+ * for Pointer 0 only is generated after.
+ * Suppose we are only interested in learning the movement of Pointer 0. If we only observe MOVE
+ * events, we will not miss any information.
+ * Even though the Pointer 1 up event contains updated Pointer 0 coordinates, there is another MOVE
+ * event generated afterwards that contains the newest movement of pointer 0.
+ * This is important for palm rejection. If there is a subsequent InputListener stage that detects
+ * palms, and wants to cancel Pointer 1, then it is safe to simply drop POINTER_1_UP event without
+ * losing information about non-palm pointers.
+ */
+TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(AMOTION_EVENT_ACTION_DOWN, {centerPoint});
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(ACTION_POINTER_1_DOWN, {centerPoint, secondPoint});
+
+    // ACTION_MOVE (First slot)
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendMove(centerPoint + Point(5, 5));
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendPointerUp();
+    // Send a single sync for the above 2 pointer updates
+    mDevice->sendSync();
+
+    // First, we should get POINTER_UP for the second pointer
+    assertReceivedMotion(ACTION_POINTER_1_UP,
+                         {/*first pointer */ centerPoint + Point(5, 5),
+                          /*second pointer*/ secondPoint});
+
+    // Next, the MOVE event for the first pointer
+    assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)});
+}
+
+/**
+ * Similar scenario as above. The difference is that when the second pointer goes up, it will first
+ * move, and then it will go up, all in the same frame.
+ * In this scenario, the movement of the second pointer just prior to liftoff is ignored, and never
+ * gets sent to the listener.
+ */
+TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(AMOTION_EVENT_ACTION_DOWN, {centerPoint});
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(ACTION_POINTER_1_DOWN, {centerPoint, secondPoint});
+
+    // ACTION_MOVE (First slot)
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendMove(centerPoint + Point(5, 5));
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendMove(secondPoint + Point(6, 6));
+    mDevice->sendPointerUp();
+    // Send a single sync for the above 2 pointer updates
+    mDevice->sendSync();
+
+    // First, we should get POINTER_UP for the second pointer
+    // The movement of the second pointer during the liftoff frame is ignored.
+    // The coordinates 'secondPoint + Point(6, 6)' are never sent to the listener.
+    assertReceivedMotion(ACTION_POINTER_1_UP,
+                         {/*first pointer */ centerPoint + Point(5, 5),
+                          /*second pointer*/ secondPoint});
+
+    // Next, the MOVE event for the first pointer
+    assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)});
+}
+
 TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
@@ -2354,6 +2515,7 @@
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
@@ -2362,12 +2524,13 @@
     mDevice->sendSlot(SECOND_SLOT);
     mDevice->sendTrackingId(SECOND_TRACKING_ID);
     mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              args.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
 
     // ACTION_MOVE (second slot)
     mDevice->sendMove(secondPoint + Point(1, 1));
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
@@ -2375,19 +2538,21 @@
     // a palm event.
     // Expect to receive the ACTION_POINTER_UP with cancel flag.
     mDevice->sendToolType(MT_TOOL_PALM);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              args.action);
+    ASSERT_EQ(ACTION_POINTER_1_UP, args.action);
     ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, args.flags);
 
     // Send up to second slot, expect first slot send moving.
     mDevice->sendPointerUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // Send ACTION_UP (first slot)
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendUp();
+    mDevice->sendSync();
 
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
@@ -2406,16 +2571,16 @@
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<TestInputListener> mFakeListener;
+    std::unique_ptr<TestInputListener> mFakeListener;
     std::unique_ptr<InstrumentedInputReader> mReader;
     std::shared_ptr<InputDevice> mDevice;
 
     void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new TestInputListener();
+        mFakeListener = std::make_unique<TestInputListener>();
         mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
-                                                            mFakeListener);
+                                                            *mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
@@ -2427,7 +2592,7 @@
     }
 
     void TearDown() override {
-        mFakeListener.clear();
+        mFakeListener.reset();
         mFakePolicy.clear();
     }
 };
@@ -2675,36 +2840,29 @@
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const Flags<InputDeviceClass> DEVICE_CLASSES;
     static const int32_t EVENTHUB_ID;
-    static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<TestInputListener> mFakeListener;
+    std::unique_ptr<TestInputListener> mFakeListener;
     std::unique_ptr<InstrumentedInputReader> mReader;
     std::shared_ptr<InputDevice> mDevice;
 
     virtual void SetUp(Flags<InputDeviceClass> classes) {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new TestInputListener();
+        mFakeListener = std::make_unique<TestInputListener>();
         mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
-                                                            mFakeListener);
+                                                            *mFakeListener);
         mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
     }
 
     void SetUp() override {
-        // Ensure per_window_input_rotation is enabled.
-        sysprop::InputFlingerProperties::per_window_input_rotation(true);
-
         SetUp(DEVICE_CLASSES);
     }
 
     void TearDown() override {
-        mFakeListener.clear();
+        mFakeListener.reset();
         mFakePolicy.clear();
-
-        sysprop::InputFlingerProperties::per_window_input_rotation(
-                INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE);
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
@@ -2816,8 +2974,6 @@
 const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
         Flags<InputDeviceClass>(0); // not needed for current tests
 const int32_t InputMapperTest::EVENTHUB_ID = 1;
-const std::optional<bool> InputMapperTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
-        sysprop::InputFlingerProperties::per_window_input_rotation();
 
 // --- SwitchInputMapperTest ---
 
@@ -3732,6 +3888,25 @@
               mapper2.getMetaState());
 }
 
+TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) {
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+    // Suppose we have two mappers. (DPAD + KEYBOARD)
+    addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_DPAD,
+                                               AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Initialize metastate to AMETA_NUM_LOCK_ON.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+
+    mReader->toggleCapsLockState(DEVICE_ID);
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+}
+
 // --- KeyboardInputMapperTest_ExternalDevice ---
 
 class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
@@ -3828,7 +4003,7 @@
         InputMapperTest::SetUp();
 
         mFakePointerController = std::make_shared<FakePointerController>();
-        mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
+        mFakePolicy->setPointerController(mFakePointerController);
     }
 
     void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
@@ -6254,6 +6429,172 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
+// --- TouchDisplayProjectionTest ---
+
+class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
+public:
+    // The values inside DisplayViewport are expected to be pre-rotated. This updates the current
+    // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
+    // rotated equivalent of the given un-rotated physical display bounds.
+    void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+        uint32_t inverseRotationFlags;
+        auto width = DISPLAY_WIDTH;
+        auto height = DISPLAY_HEIGHT;
+        switch (orientation) {
+            case DISPLAY_ORIENTATION_90:
+                inverseRotationFlags = ui::Transform::ROT_270;
+                std::swap(width, height);
+                break;
+            case DISPLAY_ORIENTATION_180:
+                inverseRotationFlags = ui::Transform::ROT_180;
+                break;
+            case DISPLAY_ORIENTATION_270:
+                inverseRotationFlags = ui::Transform::ROT_90;
+                std::swap(width, height);
+                break;
+            case DISPLAY_ORIENTATION_0:
+                inverseRotationFlags = ui::Transform::ROT_0;
+                break;
+            default:
+                FAIL() << "Invalid orientation: " << orientation;
+        }
+
+        const ui::Transform rotation(inverseRotationFlags, width, height);
+        const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay);
+
+        std::optional<DisplayViewport> internalViewport =
+                *mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+        DisplayViewport& v = *internalViewport;
+        v.displayId = DISPLAY_ID;
+        v.orientation = orientation;
+
+        v.logicalLeft = 0;
+        v.logicalTop = 0;
+        v.logicalRight = 100;
+        v.logicalBottom = 100;
+
+        v.physicalLeft = rotatedPhysicalDisplay.left;
+        v.physicalTop = rotatedPhysicalDisplay.top;
+        v.physicalRight = rotatedPhysicalDisplay.right;
+        v.physicalBottom = rotatedPhysicalDisplay.bottom;
+
+        v.deviceWidth = width;
+        v.deviceHeight = height;
+
+        v.isActive = true;
+        v.uniqueId = UNIQUE_ID;
+        v.type = ViewportType::INTERNAL;
+        mFakePolicy->updateViewport(v);
+        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    void assertReceivedMove(const Point& point) {
+        NotifyMotionArgs motionArgs;
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+        ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+        ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+        ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], point.x, point.y,
+                                                    1, 0, 0, 0, 0, 0, 0, 0));
+    }
+};
+
+TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+
+    prepareButtons();
+    prepareAxes(POSITION);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // Configure the DisplayViewport such that the logical display maps to a subsection of
+    // the display panel called the physical display. Here, the physical display is bounded by the
+    // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+    static const Rect kPhysicalDisplay{10, 20, 70, 160};
+    static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
+            {{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
+
+    for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+                             DISPLAY_ORIENTATION_270}) {
+        configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+        // Touches outside the physical display should be ignored, and should not generate any
+        // events. Ensure touches at the following points that lie outside of the physical display
+        // area do not generate any events.
+        for (const auto& point : kPointsOutsidePhysicalDisplay) {
+            processDown(mapper, toRawX(point.x), toRawY(point.y));
+            processSync(mapper);
+            processUp(mapper);
+            processSync(mapper);
+            ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled())
+                    << "Unexpected event generated for touch outside physical display at point: "
+                    << point.x << ", " << point.y;
+        }
+    }
+}
+
+TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+
+    prepareButtons();
+    prepareAxes(POSITION);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // Configure the DisplayViewport such that the logical display maps to a subsection of
+    // the display panel called the physical display. Here, the physical display is bounded by the
+    // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+    static const Rect kPhysicalDisplay{10, 20, 70, 160};
+
+    for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+                             DISPLAY_ORIENTATION_270}) {
+        configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+        // Touches that start outside the physical display should be ignored until it enters the
+        // physical display bounds, at which point it should generate a down event. Start a touch at
+        // the point (5, 100), which is outside the physical display bounds.
+        static const Point kOutsidePoint{5, 100};
+        processDown(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+        // Move the touch into the physical display area. This should generate a pointer down.
+        processMove(mapper, toRawX(11), toRawY(21));
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+        ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+        ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+        ASSERT_NO_FATAL_FAILURE(
+                assertPointerCoords(motionArgs.pointerCoords[0], 11, 21, 1, 0, 0, 0, 0, 0, 0, 0));
+
+        // Move the touch inside the physical display area. This should generate a pointer move.
+        processMove(mapper, toRawX(69), toRawY(159));
+        processSync(mapper);
+        assertReceivedMove({69, 159});
+
+        // Move outside the physical display area. Since the pointer is already down, this should
+        // now continue generating events.
+        processMove(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+        processSync(mapper);
+        assertReceivedMove(kOutsidePoint);
+
+        // Release. This should generate a pointer up.
+        processUp(mapper);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+        ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+        ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], kOutsidePoint.x,
+                                                    kOutsidePoint.y, 1, 0, 0, 0, 0, 0, 0, 0));
+
+        // Ensure no more events were generated.
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+    }
+}
+
 // --- MultiTouchInputMapperTest ---
 
 class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -6293,7 +6634,7 @@
         mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX,
                                        0, 0);
         if (axes & MINOR) {
-            mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MAX,
+            mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MIN,
                                            RAW_TOOL_MAX, 0, 0);
         }
     }
@@ -6427,8 +6768,7 @@
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
     ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
     ASSERT_EQ(0, motionArgs.flags);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -6488,8 +6828,7 @@
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
     ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
     ASSERT_EQ(0, motionArgs.flags);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -6564,8 +6903,7 @@
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
     ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_DOWN, motionArgs.action);
     ASSERT_EQ(0, motionArgs.flags);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -6594,8 +6932,7 @@
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
     ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
     ASSERT_EQ(0, motionArgs.flags);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -6660,6 +6997,57 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
+TEST_F(MultiTouchInputMapperTest, AxisResolution_IsPopulated) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
+                                   /*fuzz*/ 0, /*resolution*/ 10);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, /*flat*/ 0,
+                                   /*fuzz*/ 0, /*resolution*/ 11);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN, RAW_TOUCH_MAX,
+                                   /*flat*/ 0, /*fuzz*/ 0, /*resolution*/ 12);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, RAW_TOUCH_MIN, RAW_TOUCH_MAX,
+                                   /*flat*/ 0, /*fuzz*/ 0, /*resolution*/ 13);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX,
+                                   /*flat*/ 0, /*flat*/ 0, /*resolution*/ 14);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MIN, RAW_TOOL_MAX,
+                                   /*flat*/ 0, /*flat*/ 0, /*resolution*/ 15);
+
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // X and Y axes
+    assertAxisResolution(mapper, AMOTION_EVENT_AXIS_X, 10 / X_PRECISION);
+    assertAxisResolution(mapper, AMOTION_EVENT_AXIS_Y, 11 / Y_PRECISION);
+    // Touch major and minor
+    assertAxisResolution(mapper, AMOTION_EVENT_AXIS_TOUCH_MAJOR, 12 * GEOMETRIC_SCALE);
+    assertAxisResolution(mapper, AMOTION_EVENT_AXIS_TOUCH_MINOR, 13 * GEOMETRIC_SCALE);
+    // Tool major and minor
+    assertAxisResolution(mapper, AMOTION_EVENT_AXIS_TOOL_MAJOR, 14 * GEOMETRIC_SCALE);
+    assertAxisResolution(mapper, AMOTION_EVENT_AXIS_TOOL_MINOR, 15 * GEOMETRIC_SCALE);
+}
+
+TEST_F(MultiTouchInputMapperTest, TouchMajorAndMinorAxes_DoNotAppearIfNotSupported) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
+                                   /*fuzz*/ 0, /*resolution*/ 10);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, /*flat*/ 0,
+                                   /*fuzz*/ 0, /*resolution*/ 11);
+
+    // We do not add ABS_MT_TOUCH_MAJOR / MINOR or ABS_MT_WIDTH_MAJOR / MINOR axes
+
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Touch major and minor
+    assertAxisNotPresent(mapper, AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+    assertAxisNotPresent(mapper, AMOTION_EVENT_AXIS_TOUCH_MINOR);
+    // Tool major and minor
+    assertAxisNotPresent(mapper, AMOTION_EVENT_AXIS_TOOL_MAJOR);
+    assertAxisNotPresent(mapper, AMOTION_EVENT_AXIS_TOOL_MINOR);
+}
+
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -6690,8 +7078,7 @@
             toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -6732,8 +7119,7 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -6778,8 +7164,7 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_DOWN, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -6798,8 +7183,7 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -6864,8 +7248,7 @@
             toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -6905,8 +7288,7 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -6947,8 +7329,7 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_DOWN, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -6968,8 +7349,7 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
@@ -7134,8 +7514,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            args.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
     ASSERT_EQ(size_t(2), args.pointerCount);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             x, y, 1.0f, size, touch, touch, tool, tool, 0, 0));
@@ -7793,7 +8172,7 @@
     fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     fakePointerController->setPosition(100, 200);
     fakePointerController->setButtonState(0);
-    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+    mFakePolicy->setPointerController(fakePointerController);
 
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
     prepareSecondaryDisplay(ViewportType::EXTERNAL);
@@ -7946,8 +8325,7 @@
     // Setup PointerController.
     std::shared_ptr<FakePointerController> fakePointerController =
             std::make_shared<FakePointerController>();
-    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
-    mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController);
+    mFakePolicy->setPointerController(fakePointerController);
 
     // Setup policy for associated displays and show touches.
     const uint8_t hdmi1 = 0;
@@ -8266,8 +8644,7 @@
     processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
 
     // If the tool type of the first finger changes to MT_TOOL_PALM,
@@ -8277,8 +8654,7 @@
     processToolType(mapper, MT_TOOL_PALM);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
 
     // The following MOVE events of second finger should be processed.
@@ -8343,8 +8719,7 @@
     processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 
     // If the tool type of the first finger changes to MT_TOOL_PALM,
@@ -8354,8 +8729,7 @@
     processToolType(mapper, MT_TOOL_PALM);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
 
     // Second finger keeps moving.
@@ -8443,8 +8817,7 @@
     processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 
     // If the tool type of the second finger changes to MT_TOOL_PALM,
@@ -8453,8 +8826,7 @@
     processToolType(mapper, MT_TOOL_PALM);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
 
     // The following MOVE event should be processed.
@@ -8528,8 +8900,7 @@
     processPressure(mapper, RAW_PRESSURE_MAX);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
     ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
 
     // second finger up with some unexpected data.
@@ -8538,8 +8909,7 @@
     processPosition(mapper, x2, y2);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-              motionArgs.action);
+    ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
     ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
 
     // first finger up with some unexpected data.
@@ -8588,173 +8958,6 @@
     ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
 }
 
-/**
- * Test touch should not work if outside of surface.
- */
-class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest {
-protected:
-    void halfDisplayToCenterHorizontal(int32_t orientation) {
-        std::optional<DisplayViewport> internalViewport =
-                mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
-
-        // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
-        internalViewport->orientation = orientation;
-        if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) {
-            internalViewport->logicalLeft = 0;
-            internalViewport->logicalTop = 0;
-            internalViewport->logicalRight = DISPLAY_HEIGHT;
-            internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
-
-            internalViewport->physicalLeft = 0;
-            internalViewport->physicalTop = DISPLAY_WIDTH / 4;
-            internalViewport->physicalRight = DISPLAY_HEIGHT;
-            internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4;
-
-            internalViewport->deviceWidth = DISPLAY_HEIGHT;
-            internalViewport->deviceHeight = DISPLAY_WIDTH;
-        } else {
-            internalViewport->logicalLeft = 0;
-            internalViewport->logicalTop = 0;
-            internalViewport->logicalRight = DISPLAY_WIDTH / 2;
-            internalViewport->logicalBottom = DISPLAY_HEIGHT;
-
-            internalViewport->physicalLeft = DISPLAY_WIDTH / 4;
-            internalViewport->physicalTop = 0;
-            internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4;
-            internalViewport->physicalBottom = DISPLAY_HEIGHT;
-
-            internalViewport->deviceWidth = DISPLAY_WIDTH;
-            internalViewport->deviceHeight = DISPLAY_HEIGHT;
-        }
-
-        mFakePolicy->updateViewport(internalViewport.value());
-        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-    }
-
-    void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xOutside, int32_t yOutside,
-                                  int32_t xInside, int32_t yInside, int32_t xExpected,
-                                  int32_t yExpected) {
-        // touch on outside area should not work.
-        processPosition(mapper, toRawX(xOutside), toRawY(yOutside));
-        processSync(mapper);
-        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
-        // touch on inside area should receive the event.
-        NotifyMotionArgs args;
-        processPosition(mapper, toRawX(xInside), toRawY(yInside));
-        processSync(mapper);
-        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-        ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
-        ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
-        // Reset.
-        mapper.reset(ARBITRARY_TIME);
-    }
-};
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) {
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
-    // Touch on center of normal display should work.
-    const int32_t x = DISPLAY_WIDTH / 4;
-    const int32_t y = DISPLAY_HEIGHT / 2;
-    processPosition(mapper, toRawX(x), toRawY(y));
-    processSync(mapper);
-    NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f,
-                                                0.0f, 0.0f, 0.0f, 0.0f));
-    // Reset.
-    mapper.reset(ARBITRARY_TIME);
-
-    // Let physical display be different to device, and make surface and physical could be 1:1 in
-    // all four orientations.
-    for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
-                            DISPLAY_ORIENTATION_270}) {
-        halfDisplayToCenterHorizontal(orientation);
-
-        const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
-        const int32_t yExpected = y;
-        processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-    }
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) {
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    // Since InputReader works in the un-rotated coordinate space, only devices that are not
-    // orientation-aware are affected by display rotation.
-    addConfigurationProperty("touch.orientationAware", "0");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
-    // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
-    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90);
-
-    const int32_t x = DISPLAY_WIDTH / 4;
-    const int32_t y = DISPLAY_HEIGHT / 2;
-
-    // expect x/y = swap x/y then reverse x.
-    constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
-    constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
-    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) {
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    // Since InputReader works in the un-rotated coordinate space, only devices that are not
-    // orientation-aware are affected by display rotation.
-    addConfigurationProperty("touch.orientationAware", "0");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
-    // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
-    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
-
-    const int32_t x = DISPLAY_WIDTH / 4;
-    const int32_t y = DISPLAY_HEIGHT / 2;
-
-    // expect x/y = swap x/y then reverse y.
-    const int32_t xExpected = y;
-    const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
-    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) {
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    // Since InputReader works in the un-rotated coordinate space, only devices that are not
-    // orientation-aware are affected by display rotation.
-    addConfigurationProperty("touch.orientationAware", "0");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
-    const int32_t x = 0;
-    const int32_t y = 0;
-
-    const int32_t xExpected = x;
-    const int32_t yExpected = y;
-    processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected);
-
-    clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_90);
-    // expect x/y = swap x/y then reverse x.
-    const int32_t xExpected90 = DISPLAY_HEIGHT - 1;
-    const int32_t yExpected90 = x;
-    processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
-
-    clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_270);
-    // expect x/y = swap x/y then reverse y.
-    const int32_t xExpected270 = y;
-    const int32_t yExpected270 = DISPLAY_WIDTH - 1;
-    processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
-}
-
 TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
     // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
     std::shared_ptr<FakePointerController> fakePointerController =
@@ -8769,7 +8972,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerCapture(true);
-    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+    mFakePolicy->setPointerController(fakePointerController);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // captured touchpad should be a touchpad source
@@ -8818,8 +9021,7 @@
 
     // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            args.action);
+    ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
     ASSERT_EQ(2U, args.pointerCount);
     ASSERT_EQ(0, args.pointerProperties[0].id);
     ASSERT_EQ(1, args.pointerProperties[1].id);
@@ -8919,7 +9121,7 @@
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
-    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+    mFakePolicy->setPointerController(fakePointerController);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     // run uncaptured pointer tests - pushes out generic events
     // FINGER 0 DOWN
@@ -8959,7 +9161,7 @@
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
-    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+    mFakePolicy->setPointerController(fakePointerController);
     mFakePolicy->setPointerCapture(false);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -8986,23 +9188,23 @@
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<TestInputListener> mFakeListener;
+    std::unique_ptr<TestInputListener> mFakeListener;
     std::unique_ptr<InstrumentedInputReader> mReader;
     std::shared_ptr<InputDevice> mDevice;
 
     virtual void SetUp(Flags<InputDeviceClass> classes) {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new TestInputListener();
+        mFakeListener = std::make_unique<TestInputListener>();
         mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
-                                                            mFakeListener);
+                                                            *mFakeListener);
         mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
     }
 
     void SetUp() override { SetUp(DEVICE_CLASSES); }
 
     void TearDown() override {
-        mFakeListener.clear();
+        mFakeListener.reset();
         mFakePolicy.clear();
     }
 
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 0a1dc4b..626cdfc 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -28,12 +28,10 @@
 // --- TestInputListener ---
 
 class TestInputListener : public InputListenerInterface {
-protected:
-    virtual ~TestInputListener();
-
 public:
     TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms,
                       std::chrono::milliseconds eventDidNotHappenTimeout = 0ms);
+    virtual ~TestInputListener();
 
     void assertNotifyConfigurationChangedWasCalled(
             NotifyConfigurationChangedArgs* outEventArgs = nullptr);
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 7fec2c8..132b877 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -170,28 +170,27 @@
     injectEvent(EV_KEY, BTN_TOUCH, 1);
     injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
     injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendMove(const Point& point) {
     injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
     injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendPointerUp() {
     sendTrackingId(0xffffffff);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendUp() {
     sendTrackingId(0xffffffff);
     injectEvent(EV_KEY, BTN_TOUCH, 0);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendToolType(int32_t toolType) {
     injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+}
+
+void UinputTouchScreen::sendSync() {
     injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 01a557c..a37fc2b 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -142,6 +142,7 @@
     void sendPointerUp();
     void sendUp();
     void sendToolType(int32_t toolType);
+    void sendSync();
 
     const Point getCenterPoint();
 
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index d828aa9..6fbba3f 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -24,11 +24,11 @@
     ],
 
     aidl: {
-       local_include_dirs: ["include"],
-       include_dirs: [
-           "frameworks/base/core/java/android/os",
-       ],
-       export_aidl_headers: true
+        local_include_dirs: ["include"],
+        include_dirs: [
+            "frameworks/base/core/java/android/os",
+        ],
+        export_aidl_headers: true,
     },
 
     shared_libs: [
@@ -38,7 +38,7 @@
         "libutils",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-V2-cpp",
+        "android.hardware.power-V3-cpp",
     ],
 
     cflags: [
@@ -50,6 +50,6 @@
 
     local_include_dirs: ["include"],
     export_include_dirs: [
-         "include",
+        "include",
     ],
 }
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 3997929..fcb012f 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -38,7 +38,7 @@
         "libutils",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-V2-cpp",
+        "android.hardware.power-V3-cpp",
     ],
     static_libs: [
         "libtestUtil",
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 659b2d2..2d1558a 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -46,7 +46,7 @@
         "libutils",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-V2-cpp",
+        "android.hardware.power-V3-cpp",
     ],
     static_libs: [
         "libgmock",
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index d890f5c..cb1a77a 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -183,6 +183,10 @@
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
     ASSERT_TRUE(result.isUnsupported());
+
+    EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::CAMERA_STREAMING_HIGH), _))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
     result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
     ASSERT_TRUE(result.isUnsupported());
 }
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
new file mode 100644
index 0000000..049d06a
--- /dev/null
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AidlSensorHalWrapper.h"
+#include "ISensorsWrapper.h"
+#include "SensorDeviceUtils.h"
+#include "android/hardware/sensors/2.0/types.h"
+
+#include <aidl/android/hardware/sensors/BnSensorsCallback.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+
+using ::aidl::android::hardware::sensors::AdditionalInfo;
+using ::aidl::android::hardware::sensors::DynamicSensorInfo;
+using ::aidl::android::hardware::sensors::Event;
+using ::aidl::android::hardware::sensors::ISensors;
+using ::aidl::android::hardware::sensors::SensorInfo;
+using ::aidl::android::hardware::sensors::SensorStatus;
+using ::aidl::android::hardware::sensors::SensorType;
+using ::android::AidlMessageQueue;
+using ::android::hardware::EventFlag;
+using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+
+namespace android {
+
+namespace {
+
+status_t convertToStatus(ndk::ScopedAStatus status) {
+    if (status.isOk()) {
+        return OK;
+    } else {
+        switch (status.getExceptionCode()) {
+            case EX_ILLEGAL_ARGUMENT: {
+                return BAD_VALUE;
+            }
+            case EX_SECURITY: {
+                return PERMISSION_DENIED;
+            }
+            case EX_UNSUPPORTED_OPERATION: {
+                return INVALID_OPERATION;
+            }
+            case EX_SERVICE_SPECIFIC: {
+                switch (status.getServiceSpecificError()) {
+                    case ISensors::ERROR_BAD_VALUE: {
+                        return BAD_VALUE;
+                    }
+                    case ISensors::ERROR_NO_MEMORY: {
+                        return NO_MEMORY;
+                    }
+                    default: {
+                        return UNKNOWN_ERROR;
+                    }
+                }
+            }
+            default: {
+                return UNKNOWN_ERROR;
+            }
+        }
+    }
+}
+
+void convertToSensor(const SensorInfo &src, sensor_t *dst) {
+    dst->name = strdup(src.name.c_str());
+    dst->vendor = strdup(src.vendor.c_str());
+    dst->version = src.version;
+    dst->handle = src.sensorHandle;
+    dst->type = (int)src.type;
+    dst->maxRange = src.maxRange;
+    dst->resolution = src.resolution;
+    dst->power = src.power;
+    dst->minDelay = src.minDelayUs;
+    dst->fifoReservedEventCount = src.fifoReservedEventCount;
+    dst->fifoMaxEventCount = src.fifoMaxEventCount;
+    dst->stringType = strdup(src.typeAsString.c_str());
+    dst->requiredPermission = strdup(src.requiredPermission.c_str());
+    dst->maxDelay = src.maxDelayUs;
+    dst->flags = src.flags;
+    dst->reserved[0] = dst->reserved[1] = 0;
+}
+
+void convertToSensorEvent(const Event &src, sensors_event_t *dst) {
+    *dst = {.version = sizeof(sensors_event_t),
+            .sensor = src.sensorHandle,
+            .type = (int32_t)src.sensorType,
+            .reserved0 = 0,
+            .timestamp = src.timestamp};
+
+    switch (src.sensorType) {
+        case SensorType::META_DATA: {
+            // Legacy HALs expect the handle reference in the meta data field.
+            // Copy it over from the handle of the event.
+            dst->meta_data.what = (int32_t)src.payload.get<Event::EventPayload::meta>().what;
+            dst->meta_data.sensor = src.sensorHandle;
+            // Set the sensor handle to 0 to maintain compatibility.
+            dst->sensor = 0;
+            break;
+        }
+
+        case SensorType::ACCELEROMETER:
+        case SensorType::MAGNETIC_FIELD:
+        case SensorType::ORIENTATION:
+        case SensorType::GYROSCOPE:
+        case SensorType::GRAVITY:
+        case SensorType::LINEAR_ACCELERATION: {
+            dst->acceleration.x = src.payload.get<Event::EventPayload::vec3>().x;
+            dst->acceleration.y = src.payload.get<Event::EventPayload::vec3>().y;
+            dst->acceleration.z = src.payload.get<Event::EventPayload::vec3>().z;
+            dst->acceleration.status = (int32_t)src.payload.get<Event::EventPayload::vec3>().status;
+            break;
+        }
+
+        case SensorType::GAME_ROTATION_VECTOR: {
+            dst->data[0] = src.payload.get<Event::EventPayload::vec4>().x;
+            dst->data[1] = src.payload.get<Event::EventPayload::vec4>().y;
+            dst->data[2] = src.payload.get<Event::EventPayload::vec4>().z;
+            dst->data[3] = src.payload.get<Event::EventPayload::vec4>().w;
+            break;
+        }
+
+        case SensorType::ROTATION_VECTOR:
+        case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
+            dst->data[0] = src.payload.get<Event::EventPayload::data>().values[0];
+            dst->data[1] = src.payload.get<Event::EventPayload::data>().values[1];
+            dst->data[2] = src.payload.get<Event::EventPayload::data>().values[2];
+            dst->data[3] = src.payload.get<Event::EventPayload::data>().values[3];
+            dst->data[4] = src.payload.get<Event::EventPayload::data>().values[4];
+            break;
+        }
+
+        case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
+        case SensorType::GYROSCOPE_UNCALIBRATED:
+        case SensorType::ACCELEROMETER_UNCALIBRATED: {
+            dst->uncalibrated_gyro.x_uncalib = src.payload.get<Event::EventPayload::uncal>().x;
+            dst->uncalibrated_gyro.y_uncalib = src.payload.get<Event::EventPayload::uncal>().y;
+            dst->uncalibrated_gyro.z_uncalib = src.payload.get<Event::EventPayload::uncal>().z;
+            dst->uncalibrated_gyro.x_bias = src.payload.get<Event::EventPayload::uncal>().xBias;
+            dst->uncalibrated_gyro.y_bias = src.payload.get<Event::EventPayload::uncal>().yBias;
+            dst->uncalibrated_gyro.z_bias = src.payload.get<Event::EventPayload::uncal>().zBias;
+            break;
+        }
+
+        case SensorType::HINGE_ANGLE:
+        case SensorType::DEVICE_ORIENTATION:
+        case SensorType::LIGHT:
+        case SensorType::PRESSURE:
+        case SensorType::PROXIMITY:
+        case SensorType::RELATIVE_HUMIDITY:
+        case SensorType::AMBIENT_TEMPERATURE:
+        case SensorType::SIGNIFICANT_MOTION:
+        case SensorType::STEP_DETECTOR:
+        case SensorType::TILT_DETECTOR:
+        case SensorType::WAKE_GESTURE:
+        case SensorType::GLANCE_GESTURE:
+        case SensorType::PICK_UP_GESTURE:
+        case SensorType::WRIST_TILT_GESTURE:
+        case SensorType::STATIONARY_DETECT:
+        case SensorType::MOTION_DETECT:
+        case SensorType::HEART_BEAT:
+        case SensorType::LOW_LATENCY_OFFBODY_DETECT: {
+            dst->data[0] = src.payload.get<Event::EventPayload::scalar>();
+            break;
+        }
+
+        case SensorType::STEP_COUNTER: {
+            dst->u64.step_counter = src.payload.get<Event::EventPayload::stepCount>();
+            break;
+        }
+
+        case SensorType::HEART_RATE: {
+            dst->heart_rate.bpm = src.payload.get<Event::EventPayload::heartRate>().bpm;
+            dst->heart_rate.status =
+                    (int8_t)src.payload.get<Event::EventPayload::heartRate>().status;
+            break;
+        }
+
+        case SensorType::POSE_6DOF: { // 15 floats
+            for (size_t i = 0; i < 15; ++i) {
+                dst->data[i] = src.payload.get<Event::EventPayload::pose6DOF>().values[i];
+            }
+            break;
+        }
+
+        case SensorType::DYNAMIC_SENSOR_META: {
+            dst->dynamic_sensor_meta.connected =
+                    src.payload.get<Event::EventPayload::dynamic>().connected;
+            dst->dynamic_sensor_meta.handle =
+                    src.payload.get<Event::EventPayload::dynamic>().sensorHandle;
+            dst->dynamic_sensor_meta.sensor = NULL; // to be filled in later
+
+            memcpy(dst->dynamic_sensor_meta.uuid,
+                   src.payload.get<Event::EventPayload::dynamic>().uuid.values.data(), 16);
+
+            break;
+        }
+
+        case SensorType::ADDITIONAL_INFO: {
+            const AdditionalInfo &srcInfo = src.payload.get<Event::EventPayload::additional>();
+
+            additional_info_event_t *dstInfo = &dst->additional_info;
+            dstInfo->type = (int32_t)srcInfo.type;
+            dstInfo->serial = srcInfo.serial;
+
+            switch (srcInfo.payload.getTag()) {
+                case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: {
+                    const auto &values =
+                            srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>()
+                                    .values;
+                    CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32));
+                    memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32));
+                    break;
+                }
+                case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: {
+                    const auto &values =
+                            srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>()
+                                    .values;
+                    CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float));
+                    memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float));
+                    break;
+                }
+                default: {
+                    ALOGE("Invalid sensor additional info tag: %d", srcInfo.payload.getTag());
+                }
+            }
+            break;
+        }
+
+        default: {
+            CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
+
+            memcpy(dst->data, src.payload.get<Event::EventPayload::data>().values.data(),
+                   16 * sizeof(float));
+            break;
+        }
+    }
+}
+
+void convertFromSensorEvent(const sensors_event_t &src, Event *dst) {
+    *dst = {
+            .timestamp = src.timestamp,
+            .sensorHandle = src.sensor,
+            .sensorType = (SensorType) src.type,
+    };
+
+    switch (dst->sensorType) {
+        case SensorType::META_DATA: {
+            Event::EventPayload::MetaData meta;
+            meta.what = (Event::EventPayload::MetaData::MetaDataEventType)src.meta_data.what;
+            // Legacy HALs contain the handle reference in the meta data field.
+            // Copy that over to the handle of the event. In legacy HALs this
+            // field was expected to be 0.
+            dst->sensorHandle = src.meta_data.sensor;
+            dst->payload.set<Event::EventPayload::Tag::meta>(meta);
+            break;
+        }
+
+        case SensorType::ACCELEROMETER:
+        case SensorType::MAGNETIC_FIELD:
+        case SensorType::ORIENTATION:
+        case SensorType::GYROSCOPE:
+        case SensorType::GRAVITY:
+        case SensorType::LINEAR_ACCELERATION: {
+            Event::EventPayload::Vec3 vec3;
+            vec3.x = src.acceleration.x;
+            vec3.y = src.acceleration.y;
+            vec3.z = src.acceleration.z;
+            vec3.status = (SensorStatus)src.acceleration.status;
+            dst->payload.set<Event::EventPayload::Tag::vec3>(vec3);
+            break;
+        }
+
+        case SensorType::GAME_ROTATION_VECTOR: {
+            Event::EventPayload::Vec4 vec4;
+            vec4.x = src.data[0];
+            vec4.y = src.data[1];
+            vec4.z = src.data[2];
+            vec4.w = src.data[3];
+            dst->payload.set<Event::EventPayload::Tag::vec4>(vec4);
+            break;
+        }
+
+        case SensorType::ROTATION_VECTOR:
+        case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
+            Event::EventPayload::Data data;
+            memcpy(data.values.data(), src.data, 5 * sizeof(float));
+            dst->payload.set<Event::EventPayload::Tag::data>(data);
+            break;
+        }
+
+        case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
+        case SensorType::GYROSCOPE_UNCALIBRATED:
+        case SensorType::ACCELEROMETER_UNCALIBRATED: {
+            Event::EventPayload::Uncal uncal;
+            uncal.x = src.uncalibrated_gyro.x_uncalib;
+            uncal.y = src.uncalibrated_gyro.y_uncalib;
+            uncal.z = src.uncalibrated_gyro.z_uncalib;
+            uncal.xBias = src.uncalibrated_gyro.x_bias;
+            uncal.yBias = src.uncalibrated_gyro.y_bias;
+            uncal.zBias = src.uncalibrated_gyro.z_bias;
+            dst->payload.set<Event::EventPayload::Tag::uncal>(uncal);
+            break;
+        }
+
+        case SensorType::DEVICE_ORIENTATION:
+        case SensorType::LIGHT:
+        case SensorType::PRESSURE:
+        case SensorType::PROXIMITY:
+        case SensorType::RELATIVE_HUMIDITY:
+        case SensorType::AMBIENT_TEMPERATURE:
+        case SensorType::SIGNIFICANT_MOTION:
+        case SensorType::STEP_DETECTOR:
+        case SensorType::TILT_DETECTOR:
+        case SensorType::WAKE_GESTURE:
+        case SensorType::GLANCE_GESTURE:
+        case SensorType::PICK_UP_GESTURE:
+        case SensorType::WRIST_TILT_GESTURE:
+        case SensorType::STATIONARY_DETECT:
+        case SensorType::MOTION_DETECT:
+        case SensorType::HEART_BEAT:
+        case SensorType::LOW_LATENCY_OFFBODY_DETECT:
+        case SensorType::HINGE_ANGLE: {
+            dst->payload.set<Event::EventPayload::Tag::scalar>((float)src.data[0]);
+            break;
+        }
+
+        case SensorType::STEP_COUNTER: {
+            dst->payload.set<Event::EventPayload::Tag::stepCount>(src.u64.step_counter);
+            break;
+        }
+
+        case SensorType::HEART_RATE: {
+            Event::EventPayload::HeartRate heartRate;
+            heartRate.bpm = src.heart_rate.bpm;
+            heartRate.status = (SensorStatus)src.heart_rate.status;
+            dst->payload.set<Event::EventPayload::Tag::heartRate>(heartRate);
+            break;
+        }
+
+        case SensorType::POSE_6DOF: { // 15 floats
+            Event::EventPayload::Pose6Dof pose6DOF;
+            for (size_t i = 0; i < 15; ++i) {
+                pose6DOF.values[i] = src.data[i];
+            }
+            dst->payload.set<Event::EventPayload::Tag::pose6DOF>(pose6DOF);
+            break;
+        }
+
+        case SensorType::DYNAMIC_SENSOR_META: {
+            DynamicSensorInfo dynamic;
+            dynamic.connected = src.dynamic_sensor_meta.connected;
+            dynamic.sensorHandle = src.dynamic_sensor_meta.handle;
+
+            memcpy(dynamic.uuid.values.data(), src.dynamic_sensor_meta.uuid, 16);
+            dst->payload.set<Event::EventPayload::Tag::dynamic>(dynamic);
+            break;
+        }
+
+        case SensorType::ADDITIONAL_INFO: {
+            AdditionalInfo info;
+            const additional_info_event_t &srcInfo = src.additional_info;
+            info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type;
+            info.serial = srcInfo.serial;
+
+            AdditionalInfo::AdditionalInfoPayload::Int32Values data;
+            CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32));
+            memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32));
+            info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data);
+
+            dst->payload.set<Event::EventPayload::Tag::additional>(info);
+            break;
+        }
+
+        default: {
+            CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
+
+            Event::EventPayload::Data data;
+            memcpy(data.values.data(), src.data, 16 * sizeof(float));
+            dst->payload.set<Event::EventPayload::Tag::data>(data);
+            break;
+        }
+    }
+}
+
+void serviceDied(void *cookie) {
+    ALOGW("Sensors HAL died, attempting to reconnect.");
+    ((AidlSensorHalWrapper *)cookie)->prepareForReconnect();
+}
+
+template <typename EnumType>
+constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
+    return static_cast<typename std::underlying_type<EnumType>::type>(value);
+}
+
+enum EventQueueFlagBitsInternal : uint32_t {
+    INTERNAL_WAKE = 1 << 16,
+};
+
+} // anonymous namespace
+
+class AidlSensorsCallback : public ::aidl::android::hardware::sensors::BnSensorsCallback {
+public:
+    AidlSensorsCallback(AidlSensorHalWrapper::SensorDeviceCallback *sensorDeviceCallback)
+          : mSensorDeviceCallback(sensorDeviceCallback) {}
+
+    ::ndk::ScopedAStatus onDynamicSensorsConnected(
+            const std::vector<SensorInfo> &sensorInfos) override {
+        std::vector<sensor_t> sensors;
+        for (const SensorInfo &sensorInfo : sensorInfos) {
+            sensor_t sensor;
+            convertToSensor(sensorInfo, &sensor);
+            sensors.push_back(sensor);
+        }
+
+        mSensorDeviceCallback->onDynamicSensorsConnected(sensors);
+        return ::ndk::ScopedAStatus::ok();
+    }
+
+    ::ndk::ScopedAStatus onDynamicSensorsDisconnected(
+            const std::vector<int32_t> &sensorHandles) override {
+        mSensorDeviceCallback->onDynamicSensorsDisconnected(sensorHandles);
+        return ::ndk::ScopedAStatus::ok();
+    }
+
+private:
+    ISensorHalWrapper::SensorDeviceCallback *mSensorDeviceCallback;
+};
+
+AidlSensorHalWrapper::AidlSensorHalWrapper()
+      : mEventQueueFlag(nullptr),
+        mWakeLockQueueFlag(nullptr),
+        mDeathRecipient(AIBinder_DeathRecipient_new(serviceDied)) {}
+
+bool AidlSensorHalWrapper::supportsPolling() {
+    return false;
+}
+
+bool AidlSensorHalWrapper::supportsMessageQueues() {
+    return true;
+}
+
+bool AidlSensorHalWrapper::connect(SensorDeviceCallback *callback) {
+    mSensorDeviceCallback = callback;
+    mSensors = nullptr;
+
+    auto aidlServiceName = std::string() + ISensors::descriptor + "/default";
+    if (AServiceManager_isDeclared(aidlServiceName.c_str())) {
+        if (mSensors != nullptr) {
+            AIBinder_unlinkToDeath(mSensors->asBinder().get(), mDeathRecipient.get(), this);
+        }
+
+        ndk::SpAIBinder binder(AServiceManager_waitForService(aidlServiceName.c_str()));
+        if (binder.get() != nullptr) {
+            mSensors = ISensors::fromBinder(binder);
+            mEventQueue = std::make_unique<AidlMessageQueue<
+                    Event, SynchronizedReadWrite>>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
+                                                   /*configureEventFlagWord=*/true);
+
+            mWakeLockQueue = std::make_unique<AidlMessageQueue<
+                    int32_t, SynchronizedReadWrite>>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
+                                                     /*configureEventFlagWord=*/true);
+            if (mEventQueueFlag != nullptr) {
+                EventFlag::deleteEventFlag(&mEventQueueFlag);
+            }
+            EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag);
+            if (mWakeLockQueueFlag != nullptr) {
+                EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+            }
+            EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakeLockQueueFlag);
+
+            CHECK(mEventQueue != nullptr && mEventQueueFlag != nullptr &&
+                  mWakeLockQueue != nullptr && mWakeLockQueueFlag != nullptr);
+
+            mCallback = ndk::SharedRefBase::make<AidlSensorsCallback>(mSensorDeviceCallback);
+            mSensors->initialize(mEventQueue->dupeDesc(), mWakeLockQueue->dupeDesc(), mCallback);
+
+            AIBinder_linkToDeath(mSensors->asBinder().get(), mDeathRecipient.get(), this);
+        } else {
+            ALOGE("Could not connect to declared sensors AIDL HAL");
+        }
+    }
+
+    return mSensors != nullptr;
+}
+
+void AidlSensorHalWrapper::prepareForReconnect() {
+    mReconnecting = true;
+    if (mEventQueueFlag != nullptr) {
+        mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+    }
+}
+
+ssize_t AidlSensorHalWrapper::poll(sensors_event_t * /* buffer */, size_t /* count */) {
+    return 0;
+}
+
+ssize_t AidlSensorHalWrapper::pollFmq(sensors_event_t *buffer, size_t maxNumEventsToRead) {
+    ssize_t eventsRead = 0;
+    size_t availableEvents = mEventQueue->availableToRead();
+
+    if (availableEvents == 0) {
+        uint32_t eventFlagState = 0;
+
+        // Wait for events to become available. This is necessary so that the Event FMQ's read() is
+        // able to be called with the correct number of events to read. If the specified number of
+        // events is not available, then read() would return no events, possibly introducing
+        // additional latency in delivering events to applications.
+        if (mEventQueueFlag != nullptr) {
+            mEventQueueFlag->wait(asBaseType(ISensors::EVENT_QUEUE_FLAG_BITS_READ_AND_PROCESS) |
+                                          asBaseType(INTERNAL_WAKE),
+                                  &eventFlagState);
+        }
+        availableEvents = mEventQueue->availableToRead();
+
+        if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
+            ALOGD("Event FMQ internal wake, returning from poll with no events");
+            return DEAD_OBJECT;
+        }
+    }
+
+    size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
+    if (eventsToRead > 0) {
+        if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) {
+            // Notify the Sensors HAL that sensor events have been read. This is required to support
+            // the use of writeBlocking by the Sensors HAL.
+            if (mEventQueueFlag != nullptr) {
+                mEventQueueFlag->wake(asBaseType(ISensors::EVENT_QUEUE_FLAG_BITS_EVENTS_READ));
+            }
+
+            for (size_t i = 0; i < eventsToRead; i++) {
+                convertToSensorEvent(mEventBuffer[i], &buffer[i]);
+            }
+            eventsRead = eventsToRead;
+        } else {
+            ALOGW("Failed to read %zu events, currently %zu events available", eventsToRead,
+                  availableEvents);
+        }
+    }
+
+    return eventsRead;
+}
+
+std::vector<sensor_t> AidlSensorHalWrapper::getSensorsList() {
+    std::vector<sensor_t> sensorsFound;
+
+    if (mSensors != nullptr) {
+        std::vector<SensorInfo> list;
+        mSensors->getSensorsList(&list);
+        for (size_t i = 0; i < list.size(); i++) {
+            sensor_t sensor;
+            convertToSensor(list[i], &sensor);
+            sensorsFound.push_back(sensor);
+        }
+    }
+
+    return sensorsFound;
+}
+
+status_t AidlSensorHalWrapper::setOperationMode(SensorService::Mode mode) {
+    if (mSensors == nullptr) return NO_INIT;
+    return convertToStatus(mSensors->setOperationMode(static_cast<ISensors::OperationMode>(mode)));
+}
+
+status_t AidlSensorHalWrapper::activate(int32_t sensorHandle, bool enabled) {
+    if (mSensors == nullptr) return NO_INIT;
+    return convertToStatus(mSensors->activate(sensorHandle, enabled));
+}
+
+status_t AidlSensorHalWrapper::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                                     int64_t maxReportLatencyNs) {
+    if (mSensors == nullptr) return NO_INIT;
+    return convertToStatus(mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs));
+}
+
+status_t AidlSensorHalWrapper::flush(int32_t sensorHandle) {
+    if (mSensors == nullptr) return NO_INIT;
+    return convertToStatus(mSensors->flush(sensorHandle));
+}
+
+status_t AidlSensorHalWrapper::injectSensorData(const sensors_event_t *event) {
+    if (mSensors == nullptr) return NO_INIT;
+
+    Event ev;
+    convertFromSensorEvent(*event, &ev);
+    return convertToStatus(mSensors->injectSensorData(ev));
+}
+
+status_t AidlSensorHalWrapper::registerDirectChannel(const sensors_direct_mem_t *memory,
+                                                     int32_t *channelHandle) {
+    if (mSensors == nullptr) return NO_INIT;
+
+    ISensors::SharedMemInfo::SharedMemType type;
+    switch (memory->type) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+            type = ISensors::SharedMemInfo::SharedMemType::ASHMEM;
+            break;
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            type = ISensors::SharedMemInfo::SharedMemType::GRALLOC;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+        return BAD_VALUE;
+    }
+    ISensors::SharedMemInfo::SharedMemFormat format =
+            ISensors::SharedMemInfo::SharedMemFormat::SENSORS_EVENT;
+
+    ISensors::SharedMemInfo mem = {
+            .type = type,
+            .format = format,
+            .size = static_cast<int32_t>(memory->size),
+            .memoryHandle = makeToAidl(memory->handle),
+    };
+
+    return convertToStatus(mSensors->registerDirectChannel(mem, channelHandle));
+}
+
+status_t AidlSensorHalWrapper::unregisterDirectChannel(int32_t channelHandle) {
+    if (mSensors == nullptr) return NO_INIT;
+    return convertToStatus(mSensors->unregisterDirectChannel(channelHandle));
+}
+
+status_t AidlSensorHalWrapper::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+                                                      const struct sensors_direct_cfg_t *config) {
+    if (mSensors == nullptr) return NO_INIT;
+
+    ISensors::RateLevel rate;
+    switch (config->rate_level) {
+        case SENSOR_DIRECT_RATE_STOP:
+            rate = ISensors::RateLevel::STOP;
+            break;
+        case SENSOR_DIRECT_RATE_NORMAL:
+            rate = ISensors::RateLevel::NORMAL;
+            break;
+        case SENSOR_DIRECT_RATE_FAST:
+            rate = ISensors::RateLevel::FAST;
+            break;
+        case SENSOR_DIRECT_RATE_VERY_FAST:
+            rate = ISensors::RateLevel::VERY_FAST;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    int32_t token;
+    mSensors->configDirectReport(sensorHandle, channelHandle, rate, &token);
+    return token;
+}
+
+void AidlSensorHalWrapper::writeWakeLockHandled(uint32_t count) {
+    int signedCount = (int)count;
+    if (mWakeLockQueue->write(&signedCount)) {
+        mWakeLockQueueFlag->wake(asBaseType(ISensors::WAKE_LOCK_QUEUE_FLAG_BITS_DATA_WRITTEN));
+    } else {
+        ALOGW("Failed to write wake lock handled");
+    }
+}
+
+} // namespace android
diff --git a/services/sensorservice/AidlSensorHalWrapper.h b/services/sensorservice/AidlSensorHalWrapper.h
new file mode 100644
index 0000000..9f61993
--- /dev/null
+++ b/services/sensorservice/AidlSensorHalWrapper.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AIDL_SENSOR_HAL_WRAPPER_H
+#define ANDROID_AIDL_SENSOR_HAL_WRAPPER_H
+
+#include "ISensorHalWrapper.h"
+
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <fmq/AidlMessageQueue.h>
+#include <sensor/SensorEventQueue.h>
+
+namespace android {
+
+class AidlSensorHalWrapper : public ISensorHalWrapper {
+public:
+    AidlSensorHalWrapper();
+
+    ~AidlSensorHalWrapper() override {
+        if (mEventQueueFlag != nullptr) {
+            ::android::hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+            mEventQueueFlag = nullptr;
+        }
+        if (mWakeLockQueueFlag != nullptr) {
+            ::android::hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+            mWakeLockQueueFlag = nullptr;
+        }
+    }
+
+    virtual bool connect(SensorDeviceCallback *callback) override;
+
+    virtual void prepareForReconnect() override;
+
+    virtual bool supportsPolling() override;
+
+    virtual bool supportsMessageQueues() override;
+
+    virtual ssize_t poll(sensors_event_t *buffer, size_t count) override;
+
+    virtual ssize_t pollFmq(sensors_event_t *buffer, size_t count) override;
+
+    virtual std::vector<sensor_t> getSensorsList() override;
+
+    virtual status_t setOperationMode(SensorService::Mode mode) override;
+
+    virtual status_t activate(int32_t sensorHandle, bool enabled) override;
+
+    virtual status_t batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                           int64_t maxReportLatencyNs) override;
+
+    virtual status_t flush(int32_t sensorHandle) override;
+
+    virtual status_t injectSensorData(const sensors_event_t *event) override;
+
+    virtual status_t registerDirectChannel(const sensors_direct_mem_t *memory,
+                                           int32_t *channelHandle) override;
+
+    virtual status_t unregisterDirectChannel(int32_t channelHandle) override;
+
+    virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+                                            const struct sensors_direct_cfg_t *config) override;
+
+    virtual void writeWakeLockHandled(uint32_t count) override;
+
+private:
+    std::shared_ptr<aidl::android::hardware::sensors::ISensors> mSensors;
+    std::shared_ptr<::aidl::android::hardware::sensors::ISensorsCallback> mCallback;
+    std::unique_ptr<::android::AidlMessageQueue<::aidl::android::hardware::sensors::Event,
+                                                SynchronizedReadWrite>>
+            mEventQueue;
+    std::unique_ptr<::android::AidlMessageQueue<int, SynchronizedReadWrite>> mWakeLockQueue;
+    ::android::hardware::EventFlag *mEventQueueFlag;
+    ::android::hardware::EventFlag *mWakeLockQueueFlag;
+    SensorDeviceCallback *mSensorDeviceCallback;
+    std::array<::aidl::android::hardware::sensors::Event,
+               ::android::SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT>
+            mEventBuffer;
+
+    ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+
+} // namespace android
+
+#endif // ANDROID_AIDL_SENSOR_HAL_WRAPPER_H
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index e6b118b..d5b629d 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -11,10 +11,12 @@
     name: "libsensorservice",
 
     srcs: [
+        "AidlSensorHalWrapper.cpp",
         "BatteryService.cpp",
         "CorrectedGyroSensor.cpp",
         "Fusion.cpp",
         "GravitySensor.cpp",
+        "HidlSensorHalWrapper.cpp",
         "LinearAccelerationSensor.cpp",
         "OrientationSensor.cpp",
         "RecentEventLogger.cpp",
@@ -60,14 +62,19 @@
         "libbase",
         "libhidlbase",
         "libfmq",
+        "libbinder_ndk",
         "packagemanager_aidl-cpp",
         "android.hardware.sensors@1.0",
         "android.hardware.sensors@2.0",
         "android.hardware.sensors@2.1",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
     ],
 
     static_libs: [
+        "libaidlcommonsupport",
         "android.hardware.sensors@1.0-convert",
+        "android.hardware.sensors-V1-ndk",
     ],
 
     generated_headers: ["framework-cppstream-protos"],
diff --git a/services/sensorservice/HidlSensorHalWrapper.cpp b/services/sensorservice/HidlSensorHalWrapper.cpp
new file mode 100644
index 0000000..4c64e59
--- /dev/null
+++ b/services/sensorservice/HidlSensorHalWrapper.cpp
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HidlSensorHalWrapper.h"
+#include "android/hardware/sensors/2.0/types.h"
+#include "android/hardware/sensors/2.1/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/types.h"
+#include "convertV2_1.h"
+
+#include <android-base/logging.h>
+
+using android::hardware::hidl_vec;
+using android::hardware::sensors::V1_0::RateLevel;
+using android::hardware::sensors::V1_0::Result;
+using android::hardware::sensors::V1_0::SharedMemFormat;
+using android::hardware::sensors::V1_0::SharedMemInfo;
+using android::hardware::sensors::V1_0::SharedMemType;
+using android::hardware::sensors::V2_0::EventQueueFlagBits;
+using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
+using android::hardware::sensors::V2_1::Event;
+using android::hardware::sensors::V2_1::ISensorsCallback;
+using android::hardware::sensors::V2_1::implementation::convertFromSensorEvent;
+using android::hardware::sensors::V2_1::implementation::convertToNewEvents;
+using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
+using android::hardware::sensors::V2_1::implementation::convertToSensor;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1;
+
+namespace android {
+
+namespace {
+
+status_t statusFromResult(Result result) {
+    switch (result) {
+        case Result::OK:
+            return OK;
+        case Result::BAD_VALUE:
+            return BAD_VALUE;
+        case Result::PERMISSION_DENIED:
+            return PERMISSION_DENIED;
+        case Result::INVALID_OPERATION:
+            return INVALID_OPERATION;
+        case Result::NO_MEMORY:
+            return NO_MEMORY;
+    }
+}
+
+template <typename EnumType>
+constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
+    return static_cast<typename std::underlying_type<EnumType>::type>(value);
+}
+
+enum EventQueueFlagBitsInternal : uint32_t {
+    INTERNAL_WAKE = 1 << 16,
+};
+
+} // anonymous namespace
+
+void SensorsHalDeathReceiver::serviceDied(
+        uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
+    ALOGW("Sensors HAL died, attempting to reconnect.");
+    mHidlSensorHalWrapper->prepareForReconnect();
+}
+
+struct HidlSensorsCallback : public ISensorsCallback {
+    using Result = ::android::hardware::sensors::V1_0::Result;
+    using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
+
+    HidlSensorsCallback(ISensorHalWrapper::SensorDeviceCallback* sensorDeviceCallback) {
+        mSensorDeviceCallback = sensorDeviceCallback;
+    }
+
+    Return<void> onDynamicSensorsConnected_2_1(
+            const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
+        std::vector<sensor_t> sensors;
+        for (const android::hardware::sensors::V2_1::SensorInfo& info : dynamicSensorsAdded) {
+            sensor_t sensor;
+            convertToSensor(info, &sensor);
+            sensors.push_back(sensor);
+        }
+
+        mSensorDeviceCallback->onDynamicSensorsConnected(sensors);
+        return Return<void>();
+    }
+
+    Return<void> onDynamicSensorsConnected(
+            const hidl_vec<android::hardware::sensors::V1_0::SensorInfo>& dynamicSensorsAdded)
+            override {
+        return onDynamicSensorsConnected_2_1(convertToNewSensorInfos(dynamicSensorsAdded));
+    }
+
+    Return<void> onDynamicSensorsDisconnected(
+            const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+        mSensorDeviceCallback->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved);
+        return Return<void>();
+    }
+
+private:
+    ISensorHalWrapper::SensorDeviceCallback* mSensorDeviceCallback;
+};
+
+bool HidlSensorHalWrapper::supportsPolling() {
+    return mSensors->supportsPolling();
+}
+
+bool HidlSensorHalWrapper::supportsMessageQueues() {
+    return mSensors->supportsMessageQueues();
+}
+
+bool HidlSensorHalWrapper::connect(SensorDeviceCallback* callback) {
+    mSensorDeviceCallback = callback;
+    bool ret = connectHidlService();
+    if (mEventQueueFlag != nullptr) {
+        mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+    }
+    return ret;
+}
+
+void HidlSensorHalWrapper::prepareForReconnect() {
+    mReconnecting = true;
+    if (mEventQueueFlag != nullptr) {
+        mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+    }
+}
+
+ssize_t HidlSensorHalWrapper::poll(sensors_event_t* buffer, size_t count) {
+    ssize_t err;
+    int numHidlTransportErrors = 0;
+    bool hidlTransportError = false;
+
+    do {
+        auto ret = mSensors->poll(count,
+                                  [&](auto result, const auto& events,
+                                      const auto& dynamicSensorsAdded) {
+                                      if (result == Result::OK) {
+                                          convertToSensorEvents(convertToNewEvents(events),
+                                                                convertToNewSensorInfos(
+                                                                        dynamicSensorsAdded),
+                                                                buffer);
+                                          err = (ssize_t)events.size();
+                                      } else {
+                                          err = statusFromResult(result);
+                                      }
+                                  });
+
+        if (ret.isOk()) {
+            hidlTransportError = false;
+        } else {
+            hidlTransportError = true;
+            numHidlTransportErrors++;
+            if (numHidlTransportErrors > 50) {
+                // Log error and bail
+                ALOGE("Max Hidl transport errors this cycle : %d", numHidlTransportErrors);
+                handleHidlDeath(ret.description());
+            } else {
+                std::this_thread::sleep_for(std::chrono::milliseconds(10));
+            }
+        }
+    } while (hidlTransportError);
+
+    if (numHidlTransportErrors > 0) {
+        ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
+        HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors);
+        mHidlTransportErrors.add(errLog);
+        mTotalHidlTransportErrors++;
+    }
+
+    return err;
+}
+
+ssize_t HidlSensorHalWrapper::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) {
+    ssize_t eventsRead = 0;
+    size_t availableEvents = mSensors->getEventQueue()->availableToRead();
+
+    if (availableEvents == 0) {
+        uint32_t eventFlagState = 0;
+
+        // Wait for events to become available. This is necessary so that the Event FMQ's read() is
+        // able to be called with the correct number of events to read. If the specified number of
+        // events is not available, then read() would return no events, possibly introducing
+        // additional latency in delivering events to applications.
+        if (mEventQueueFlag != nullptr) {
+            mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
+                                          asBaseType(INTERNAL_WAKE),
+                                  &eventFlagState);
+        }
+        availableEvents = mSensors->getEventQueue()->availableToRead();
+
+        if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
+            ALOGD("Event FMQ internal wake, returning from poll with no events");
+            return DEAD_OBJECT;
+        }
+    }
+
+    size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
+    if (eventsToRead > 0) {
+        if (mSensors->getEventQueue()->read(mEventBuffer.data(), eventsToRead)) {
+            // Notify the Sensors HAL that sensor events have been read. This is required to support
+            // the use of writeBlocking by the Sensors HAL.
+            if (mEventQueueFlag != nullptr) {
+                mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
+            }
+
+            for (size_t i = 0; i < eventsToRead; i++) {
+                convertToSensorEvent(mEventBuffer[i], &buffer[i]);
+            }
+            eventsRead = eventsToRead;
+        } else {
+            ALOGW("Failed to read %zu events, currently %zu events available", eventsToRead,
+                  availableEvents);
+        }
+    }
+
+    return eventsRead;
+}
+
+std::vector<sensor_t> HidlSensorHalWrapper::getSensorsList() {
+    std::vector<sensor_t> sensorsFound;
+    if (mSensors != nullptr) {
+        checkReturn(mSensors->getSensorsList([&](const auto& list) {
+            for (size_t i = 0; i < list.size(); i++) {
+                sensor_t sensor;
+                convertToSensor(list[i], &sensor);
+                sensorsFound.push_back(sensor);
+
+                // Only disable all sensors on HAL 1.0 since HAL 2.0
+                // handles this in its initialize method
+                if (!mSensors->supportsMessageQueues()) {
+                    checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+                }
+            }
+        }));
+    }
+
+    return sensorsFound;
+}
+
+status_t HidlSensorHalWrapper::setOperationMode(SensorService::Mode mode) {
+    if (mSensors == nullptr) return NO_INIT;
+    return checkReturnAndGetStatus(
+            mSensors->setOperationMode(static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+}
+
+status_t HidlSensorHalWrapper::activate(int32_t sensorHandle, bool enabled) {
+    if (mSensors == nullptr) return NO_INIT;
+    return checkReturnAndGetStatus(mSensors->activate(sensorHandle, enabled));
+}
+
+status_t HidlSensorHalWrapper::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                                     int64_t maxReportLatencyNs) {
+    if (mSensors == nullptr) return NO_INIT;
+    return checkReturnAndGetStatus(
+            mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs));
+}
+
+status_t HidlSensorHalWrapper::flush(int32_t sensorHandle) {
+    if (mSensors == nullptr) return NO_INIT;
+    return checkReturnAndGetStatus(mSensors->flush(sensorHandle));
+}
+
+status_t HidlSensorHalWrapper::injectSensorData(const sensors_event_t* event) {
+    if (mSensors == nullptr) return NO_INIT;
+
+    Event ev;
+    convertFromSensorEvent(*event, &ev);
+    return checkReturnAndGetStatus(mSensors->injectSensorData(ev));
+}
+
+status_t HidlSensorHalWrapper::registerDirectChannel(const sensors_direct_mem_t* memory,
+                                                     int32_t* /*channelHandle*/) {
+    if (mSensors == nullptr) return NO_INIT;
+
+    SharedMemType type;
+    switch (memory->type) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+            type = SharedMemType::ASHMEM;
+            break;
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            type = SharedMemType::GRALLOC;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    SharedMemFormat format;
+    if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+        return BAD_VALUE;
+    }
+    format = SharedMemFormat::SENSORS_EVENT;
+
+    SharedMemInfo mem = {
+            .type = type,
+            .format = format,
+            .size = static_cast<uint32_t>(memory->size),
+            .memoryHandle = memory->handle,
+    };
+
+    status_t ret;
+    checkReturn(mSensors->registerDirectChannel(mem, [&ret](auto result, auto channelHandle) {
+        if (result == Result::OK) {
+            ret = channelHandle;
+        } else {
+            ret = statusFromResult(result);
+        }
+    }));
+    return ret;
+}
+
+status_t HidlSensorHalWrapper::unregisterDirectChannel(int32_t channelHandle) {
+    if (mSensors == nullptr) return NO_INIT;
+    return checkReturnAndGetStatus(mSensors->unregisterDirectChannel(channelHandle));
+}
+
+status_t HidlSensorHalWrapper::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+                                                      const struct sensors_direct_cfg_t* config) {
+    if (mSensors == nullptr) return NO_INIT;
+
+    RateLevel rate;
+    switch (config->rate_level) {
+        case SENSOR_DIRECT_RATE_STOP:
+            rate = RateLevel::STOP;
+            break;
+        case SENSOR_DIRECT_RATE_NORMAL:
+            rate = RateLevel::NORMAL;
+            break;
+        case SENSOR_DIRECT_RATE_FAST:
+            rate = RateLevel::FAST;
+            break;
+        case SENSOR_DIRECT_RATE_VERY_FAST:
+            rate = RateLevel::VERY_FAST;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    status_t ret;
+    checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
+                                             [&ret, rate](auto result, auto token) {
+                                                 if (rate == RateLevel::STOP) {
+                                                     ret = statusFromResult(result);
+                                                 } else {
+                                                     if (result == Result::OK) {
+                                                         ret = token;
+                                                     } else {
+                                                         ret = statusFromResult(result);
+                                                     }
+                                                 }
+                                             }));
+
+    return ret;
+}
+
+void HidlSensorHalWrapper::writeWakeLockHandled(uint32_t count) {
+    if (mWakeLockQueue->write(&count)) {
+        mWakeLockQueueFlag->wake(asBaseType(WakeLockQueueFlagBits::DATA_WRITTEN));
+    } else {
+        ALOGW("Failed to write wake lock handled");
+    }
+}
+
+status_t HidlSensorHalWrapper::checkReturnAndGetStatus(const hardware::Return<Result>& ret) {
+    checkReturn(ret);
+    return (!ret.isOk()) ? DEAD_OBJECT : statusFromResult(ret);
+}
+
+void HidlSensorHalWrapper::handleHidlDeath(const std::string& detail) {
+    if (!mSensors->supportsMessageQueues()) {
+        // restart is the only option at present.
+        LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+    } else {
+        ALOGD("ISensors HAL died, death recipient will attempt reconnect");
+    }
+}
+
+bool HidlSensorHalWrapper::connectHidlService() {
+    HalConnectionStatus status = connectHidlServiceV2_1();
+    if (status == HalConnectionStatus::DOES_NOT_EXIST) {
+        status = connectHidlServiceV2_0();
+    }
+
+    if (status == HalConnectionStatus::DOES_NOT_EXIST) {
+        status = connectHidlServiceV1_0();
+    }
+    return (status == HalConnectionStatus::CONNECTED);
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::connectHidlServiceV1_0() {
+    // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
+    size_t retry = 10;
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+
+    while (retry-- > 0) {
+        sp<android::hardware::sensors::V1_0::ISensors> sensors =
+                android::hardware::sensors::V1_0::ISensors::getService();
+        if (sensors == nullptr) {
+            // no sensor hidl service found
+            connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+            break;
+        }
+
+        mSensors = new ISensorsWrapperV1_0(sensors);
+        mRestartWaiter->reset();
+        // Poke ISensor service. If it has lingering connection from previous generation of
+        // system server, it will kill itself. There is no intention to handle the poll result,
+        // which will be done since the size is 0.
+        if (mSensors->poll(0, [](auto, const auto&, const auto&) {}).isOk()) {
+            // ok to continue
+            connectionStatus = HalConnectionStatus::CONNECTED;
+            break;
+        }
+
+        // hidl service is restarting, pointer is invalid.
+        mSensors = nullptr;
+        connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
+        ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
+        mRestartWaiter->wait();
+    }
+
+    return connectionStatus;
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::connectHidlServiceV2_0() {
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+    sp<android::hardware::sensors::V2_0::ISensors> sensors =
+            android::hardware::sensors::V2_0::ISensors::getService();
+
+    if (sensors == nullptr) {
+        connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+    } else {
+        mSensors = new ISensorsWrapperV2_0(sensors);
+        connectionStatus = initializeHidlServiceV2_X();
+    }
+
+    return connectionStatus;
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::connectHidlServiceV2_1() {
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+    sp<android::hardware::sensors::V2_1::ISensors> sensors =
+            android::hardware::sensors::V2_1::ISensors::getService();
+
+    if (sensors == nullptr) {
+        connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+    } else {
+        mSensors = new ISensorsWrapperV2_1(sensors);
+        connectionStatus = initializeHidlServiceV2_X();
+    }
+
+    return connectionStatus;
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::initializeHidlServiceV2_X() {
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+
+    mWakeLockQueue =
+            std::make_unique<WakeLockQueue>(SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+                                            true /* configureEventFlagWord */);
+
+    hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+    hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(),
+                                         &mEventQueueFlag);
+
+    hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+    hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakeLockQueueFlag);
+
+    CHECK(mSensors != nullptr && mWakeLockQueue != nullptr && mEventQueueFlag != nullptr &&
+          mWakeLockQueueFlag != nullptr);
+
+    mCallback = sp<HidlSensorsCallback>::make(mSensorDeviceCallback);
+    status_t status =
+            checkReturnAndGetStatus(mSensors->initialize(*mWakeLockQueue->getDesc(), mCallback));
+
+    if (status != NO_ERROR) {
+        connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
+        ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
+    } else {
+        connectionStatus = HalConnectionStatus::CONNECTED;
+        mSensorsHalDeathReceiver = new SensorsHalDeathReceiver(this);
+        mSensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
+    }
+
+    return connectionStatus;
+}
+
+void HidlSensorHalWrapper::convertToSensorEvent(const Event& src, sensors_event_t* dst) {
+    android::hardware::sensors::V2_1::implementation::convertToSensorEvent(src, dst);
+}
+
+void HidlSensorHalWrapper::convertToSensorEvents(const hidl_vec<Event>& src,
+                                                 const hidl_vec<SensorInfo>& dynamicSensorsAdded,
+                                                 sensors_event_t* dst) {
+    if (dynamicSensorsAdded.size() > 0 && mCallback != nullptr) {
+        mCallback->onDynamicSensorsConnected_2_1(dynamicSensorsAdded);
+    }
+
+    for (size_t i = 0; i < src.size(); ++i) {
+        convertToSensorEvent(src[i], &dst[i]);
+    }
+}
+
+} // namespace android
diff --git a/services/sensorservice/HidlSensorHalWrapper.h b/services/sensorservice/HidlSensorHalWrapper.h
new file mode 100644
index 0000000..71c3512
--- /dev/null
+++ b/services/sensorservice/HidlSensorHalWrapper.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HIDL_SENSOR_HAL_WRAPPER_H
+#define ANDROID_HIDL_SENSOR_HAL_WRAPPER_H
+
+#include <sensor/SensorEventQueue.h>
+#include <utils/Singleton.h>
+
+#include "ISensorHalWrapper.h"
+
+#include "ISensorsWrapper.h"
+#include "SensorDeviceUtils.h"
+
+namespace android {
+
+using android::hardware::sensors::V1_0::Result;
+using android::hardware::sensors::V2_1::Event;
+using android::hardware::sensors::V2_1::SensorInfo;
+
+class HidlTransportErrorLog {
+public:
+    HidlTransportErrorLog() {
+        mTs = 0;
+        mCount = 0;
+    }
+
+    HidlTransportErrorLog(time_t ts, int count) {
+        mTs = ts;
+        mCount = count;
+    }
+
+    String8 toString() const {
+        String8 result;
+        struct tm* timeInfo = localtime(&mTs);
+        result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min,
+                            timeInfo->tm_sec, mCount);
+        return result;
+    }
+
+private:
+    time_t mTs; // timestamp of the error
+    int mCount; // number of transport errors observed
+};
+
+class SensorsHalDeathReceiver : public android::hardware::hidl_death_recipient {
+public:
+    SensorsHalDeathReceiver(ISensorHalWrapper* wrapper) : mHidlSensorHalWrapper(wrapper) {}
+
+    virtual void serviceDied(uint64_t cookie,
+                             const wp<::android::hidl::base::V1_0::IBase>& service) override;
+
+private:
+    ISensorHalWrapper* mHidlSensorHalWrapper;
+};
+
+class HidlSensorHalWrapper : public ISensorHalWrapper {
+public:
+    HidlSensorHalWrapper()
+          : mHidlTransportErrors(20),
+            mTotalHidlTransportErrors(0),
+            mRestartWaiter(new SensorDeviceUtils::HidlServiceRegistrationWaiter()),
+            mEventQueueFlag(nullptr),
+            mWakeLockQueueFlag(nullptr) {}
+
+    ~HidlSensorHalWrapper() override {
+        if (mEventQueueFlag != nullptr) {
+            hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+            mEventQueueFlag = nullptr;
+        }
+        if (mWakeLockQueueFlag != nullptr) {
+            hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+            mWakeLockQueueFlag = nullptr;
+        }
+    }
+    virtual bool connect(SensorDeviceCallback* callback) override;
+
+    virtual void prepareForReconnect() override;
+
+    virtual bool supportsPolling() override;
+
+    virtual bool supportsMessageQueues() override;
+
+    virtual ssize_t poll(sensors_event_t* buffer, size_t count) override;
+
+    virtual ssize_t pollFmq(sensors_event_t* buffer, size_t count) override;
+
+    virtual std::vector<sensor_t> getSensorsList() override;
+
+    virtual status_t setOperationMode(SensorService::Mode mode) override;
+
+    virtual status_t activate(int32_t sensorHandle, bool enabled) override;
+
+    virtual status_t batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                           int64_t maxReportLatencyNs) override;
+
+    virtual status_t flush(int32_t sensorHandle) override;
+
+    virtual status_t injectSensorData(const sensors_event_t* event) override;
+
+    virtual status_t registerDirectChannel(const sensors_direct_mem_t* memory,
+                                           int32_t* channelHandle) override;
+
+    virtual status_t unregisterDirectChannel(int32_t channelHandle) override;
+
+    virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+                                            const struct sensors_direct_cfg_t* config) override;
+
+    virtual void writeWakeLockHandled(uint32_t count) override;
+
+private:
+    sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors;
+    sp<::android::hardware::sensors::V2_1::ISensorsCallback> mCallback;
+
+    // Keep track of any hidl transport failures
+    SensorServiceUtil::RingBuffer<HidlTransportErrorLog> mHidlTransportErrors;
+    int mTotalHidlTransportErrors;
+
+    SensorDeviceCallback* mSensorDeviceCallback = nullptr;
+
+    // TODO(b/67425500): remove waiter after bug is resolved.
+    sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
+
+    template <typename T>
+    void checkReturn(const hardware::Return<T>& ret) {
+        if (!ret.isOk()) {
+            handleHidlDeath(ret.description());
+        }
+    }
+
+    status_t checkReturnAndGetStatus(const hardware::Return<Result>& ret);
+
+    void handleHidlDeath(const std::string& detail);
+
+    void convertToSensorEvent(const Event& src, sensors_event_t* dst);
+
+    void convertToSensorEvents(const hardware::hidl_vec<Event>& src,
+                               const hardware::hidl_vec<SensorInfo>& dynamicSensorsAdded,
+                               sensors_event_t* dst);
+
+    bool connectHidlService();
+
+    HalConnectionStatus connectHidlServiceV1_0();
+    HalConnectionStatus connectHidlServiceV2_0();
+    HalConnectionStatus connectHidlServiceV2_1();
+    HalConnectionStatus initializeHidlServiceV2_X();
+
+    typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue;
+    std::unique_ptr<WakeLockQueue> mWakeLockQueue;
+
+    hardware::EventFlag* mEventQueueFlag;
+    hardware::EventFlag* mWakeLockQueueFlag;
+
+    std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
+
+    sp<SensorsHalDeathReceiver> mSensorsHalDeathReceiver;
+};
+
+} // namespace android
+
+#endif // ANDROID_HIDL_SENSOR_HAL_WRAPPER_H
diff --git a/services/sensorservice/ISensorHalWrapper.h b/services/sensorservice/ISensorHalWrapper.h
new file mode 100644
index 0000000..3d33540
--- /dev/null
+++ b/services/sensorservice/ISensorHalWrapper.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ISENSOR_HAL_WRAPPER_H
+#define ANDROID_ISENSOR_HAL_WRAPPER_H
+
+#include <hardware/sensors.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "SensorService.h"
+
+namespace android {
+
+/**
+ * A wrapper for various types of HAL implementation, e.g. to distinguish HIDL and AIDL versions.
+ */
+class ISensorHalWrapper {
+public:
+    class SensorDeviceCallback {
+    public:
+        virtual void onDynamicSensorsConnected(
+                const std::vector<sensor_t> &dynamicSensorsAdded) = 0;
+
+        virtual void onDynamicSensorsDisconnected(
+                const std::vector<int32_t> &dynamicSensorHandlesRemoved) = 0;
+
+        virtual ~SensorDeviceCallback(){};
+    };
+
+    enum HalConnectionStatus {
+        CONNECTED,         // Successfully connected to the HAL
+        DOES_NOT_EXIST,    // Could not find the HAL
+        FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize
+        UNKNOWN,
+    };
+
+    virtual ~ISensorHalWrapper(){};
+
+    /**
+     * Connects to the underlying sensors HAL. This should also be used for any reconnections
+     * due to HAL resets.
+     */
+    virtual bool connect(SensorDeviceCallback *callback) = 0;
+
+    virtual void prepareForReconnect() = 0;
+
+    virtual bool supportsPolling() = 0;
+
+    virtual bool supportsMessageQueues() = 0;
+
+    /**
+     * Polls for available sensor events. This could be using the traditional sensors
+     * polling or from a FMQ.
+     */
+    virtual ssize_t poll(sensors_event_t *buffer, size_t count) = 0;
+
+    virtual ssize_t pollFmq(sensors_event_t *buffer, size_t maxNumEventsToRead) = 0;
+
+    /**
+     * The below functions directly mirrors the sensors HAL definitions.
+     */
+    virtual std::vector<sensor_t> getSensorsList() = 0;
+
+    virtual status_t setOperationMode(SensorService::Mode mode) = 0;
+
+    virtual status_t activate(int32_t sensorHandle, bool enabled) = 0;
+
+    virtual status_t batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                           int64_t maxReportLatencyNs) = 0;
+
+    virtual status_t flush(int32_t sensorHandle) = 0;
+
+    virtual status_t injectSensorData(const sensors_event_t *event) = 0;
+
+    virtual status_t registerDirectChannel(const sensors_direct_mem_t *memory,
+                                           int32_t *channelHandle) = 0;
+
+    virtual status_t unregisterDirectChannel(int32_t channelHandle) = 0;
+
+    virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+                                            const struct sensors_direct_cfg_t *config) = 0;
+
+    virtual void writeWakeLockHandled(uint32_t count) = 0;
+
+    std::atomic_bool mReconnecting = false;
+};
+
+} // namespace android
+
+#endif // ANDROID_ISENSOR_HAL_WRAPPER_H
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index e4efde2..a0e30ac 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -17,38 +17,29 @@
 #include "SensorDevice.h"
 
 #include "android/hardware/sensors/2.0/types.h"
-#include "android/hardware/sensors/2.1/ISensorsCallback.h"
 #include "android/hardware/sensors/2.1/types.h"
 #include "convertV2_1.h"
 
+#include "AidlSensorHalWrapper.h"
+#include "HidlSensorHalWrapper.h"
+
 #include <android-base/logging.h>
 #include <android/util/ProtoOutputStream.h>
-#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
-#include <sensors/convert.h>
 #include <cutils/atomic.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
+#include <hardware/sensors-base.h>
+#include <hardware/sensors.h>
+#include <sensors/convert.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
-#include <cstddef>
 #include <chrono>
 #include <cinttypes>
+#include <cstddef>
 #include <thread>
 
 using namespace android::hardware::sensors;
-using namespace android::hardware::sensors::V1_0;
-using namespace android::hardware::sensors::V1_0::implementation;
-using android::hardware::sensors::V2_0::EventQueueFlagBits;
-using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
-using android::hardware::sensors::V2_1::ISensorsCallback;
-using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo;
-using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
-using android::hardware::sensors::V2_1::implementation::convertToNewEvents;
-using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0;
-using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0;
-using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1;
-using android::hardware::hidl_vec;
 using android::hardware::Return;
-using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
 using android::util::ProtoOutputStream;
 
 namespace android {
@@ -58,22 +49,7 @@
 
 namespace {
 
-status_t statusFromResult(Result result) {
-    switch (result) {
-        case Result::OK:
-            return OK;
-        case Result::BAD_VALUE:
-            return BAD_VALUE;
-        case Result::PERMISSION_DENIED:
-            return PERMISSION_DENIED;
-        case Result::INVALID_OPERATION:
-            return INVALID_OPERATION;
-        case Result::NO_MEMORY:
-            return NO_MEMORY;
-    }
-}
-
-template<typename EnumType>
+template <typename EnumType>
 constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
     return static_cast<typename std::underlying_type<EnumType>::type>(value);
 }
@@ -81,244 +57,108 @@
 // Used internally by the framework to wake the Event FMQ. These values must start after
 // the last value of EventQueueFlagBits
 enum EventQueueFlagBitsInternal : uint32_t {
-    INTERNAL_WAKE =  1 << 16,
+    INTERNAL_WAKE = 1 << 16,
 };
 
-}  // anonymous namespace
-
-void SensorsHalDeathReceivier::serviceDied(
-        uint64_t /* cookie */,
-        const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
-    ALOGW("Sensors HAL died, attempting to reconnect.");
-    SensorDevice::getInstance().prepareForReconnect();
-}
-
-struct SensorsCallback : public ISensorsCallback {
-    using Result = ::android::hardware::sensors::V1_0::Result;
-    using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
-
-    Return<void> onDynamicSensorsConnected_2_1(
-            const hidl_vec<SensorInfo> &dynamicSensorsAdded) override {
-        return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded);
-    }
-
-    Return<void> onDynamicSensorsConnected(
-            const hidl_vec<V1_0::SensorInfo> &dynamicSensorsAdded) override {
-        return SensorDevice::getInstance().onDynamicSensorsConnected(
-                convertToNewSensorInfos(dynamicSensorsAdded));
-    }
-
-    Return<void> onDynamicSensorsDisconnected(
-            const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override {
-        return SensorDevice::getInstance().onDynamicSensorsDisconnected(
-                dynamicSensorHandlesRemoved);
-    }
+enum DevicePrivateBase : int32_t {
+    DEVICE_PRIVATE_BASE = 65536,
 };
 
-SensorDevice::SensorDevice()
-        : mHidlTransportErrors(20),
-          mRestartWaiter(new HidlServiceRegistrationWaiter()),
-          mEventQueueFlag(nullptr),
-          mWakeLockQueueFlag(nullptr),
-          mReconnecting(false) {
-    if (!connectHidlService()) {
+} // anonymous namespace
+
+SensorDevice::SensorDevice() {
+    if (!connectHalService()) {
         return;
     }
 
     initializeSensorList();
 
-    mIsDirectReportSupported =
-            (checkReturnAndGetStatus(mSensors->unregisterDirectChannel(-1)) != INVALID_OPERATION);
+    mIsDirectReportSupported = (mHalWrapper->unregisterDirectChannel(-1) != INVALID_OPERATION);
 }
 
 void SensorDevice::initializeSensorList() {
-    checkReturn(mSensors->getSensorsList(
-            [&](const auto &list) {
-                const size_t count = list.size();
+    if (mHalWrapper == nullptr) {
+        return;
+    }
 
-                mActivationCount.setCapacity(count);
-                Info model;
-                for (size_t i=0 ; i < count; i++) {
-                    sensor_t sensor;
-                    convertToSensor(convertToOldSensorInfo(list[i]), &sensor);
+    auto list = mHalWrapper->getSensorsList();
+    const size_t count = list.size();
 
-                    if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
-                        sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
+    mActivationCount.setCapacity(count);
+    Info model;
+    for (size_t i = 0; i < count; i++) {
+        sensor_t sensor = list[i];
 
-                        // Some sensors don't have a default resolution and will be left at 0.
-                        // Don't crash in this case since CTS will verify that devices don't go to
-                        // production with a resolution of 0.
-                        if (sensor.resolution != 0) {
-                            float quantizedRange = sensor.maxRange;
-                            SensorDeviceUtils::quantizeValue(
-                                    &quantizedRange, sensor.resolution, /*factor=*/ 1);
-                            // Only rewrite maxRange if the requantization produced a "significant"
-                            // change, which is fairly arbitrarily defined as resolution / 8.
-                            // Smaller deltas are permitted, as they may simply be due to floating
-                            // point representation error, etc.
-                            if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
-                                ALOGW("%s's max range %.12f is not a multiple of the resolution "
-                                      "%.12f - updated to %.12f", sensor.name, sensor.maxRange,
-                                      sensor.resolution, quantizedRange);
-                                sensor.maxRange = quantizedRange;
-                            }
-                        } else {
-                            // Don't crash here or the device will go into a crashloop.
-                            ALOGW("%s should have a non-zero resolution", sensor.name);
-                        }
-                    }
+        if (sensor.type < DEVICE_PRIVATE_BASE) {
+            sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
 
-                    // Sanity check and clamp power if it is 0 (or close)
-                    constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
-                    if (sensor.power < MIN_POWER_MA) {
-                        ALOGI("%s's reported power %f invalid, clamped to %f",
-                              sensor.name, sensor.power, MIN_POWER_MA);
-                        sensor.power = MIN_POWER_MA;
-                    }
-                    mSensorList.push_back(sensor);
-
-                    mActivationCount.add(list[i].sensorHandle, model);
-
-                    // Only disable all sensors on HAL 1.0 since HAL 2.0
-                    // handles this in its initialize method
-                    if (!mSensors->supportsMessageQueues()) {
-                        checkReturn(mSensors->activate(list[i].sensorHandle,
-                                    0 /* enabled */));
-                    }
+            // Some sensors don't have a default resolution and will be left at 0.
+            // Don't crash in this case since CTS will verify that devices don't go to
+            // production with a resolution of 0.
+            if (sensor.resolution != 0) {
+                float quantizedRange = sensor.maxRange;
+                SensorDeviceUtils::quantizeValue(&quantizedRange, sensor.resolution,
+                                                 /*factor=*/1);
+                // Only rewrite maxRange if the requantization produced a "significant"
+                // change, which is fairly arbitrarily defined as resolution / 8.
+                // Smaller deltas are permitted, as they may simply be due to floating
+                // point representation error, etc.
+                if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
+                    ALOGW("%s's max range %.12f is not a multiple of the resolution "
+                          "%.12f - updated to %.12f",
+                          sensor.name, sensor.maxRange, sensor.resolution, quantizedRange);
+                    sensor.maxRange = quantizedRange;
                 }
-            }));
-}
-
-SensorDevice::~SensorDevice() {
-    if (mEventQueueFlag != nullptr) {
-        hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
-        mEventQueueFlag = nullptr;
-    }
-
-    if (mWakeLockQueueFlag != nullptr) {
-        hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
-        mWakeLockQueueFlag = nullptr;
-    }
-}
-
-bool SensorDevice::connectHidlService() {
-    HalConnectionStatus status = connectHidlServiceV2_1();
-    if (status == HalConnectionStatus::DOES_NOT_EXIST) {
-        status = connectHidlServiceV2_0();
-    }
-
-    if (status == HalConnectionStatus::DOES_NOT_EXIST) {
-        status = connectHidlServiceV1_0();
-    }
-    return (status == HalConnectionStatus::CONNECTED);
-}
-
-SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() {
-    // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
-    size_t retry = 10;
-    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
-
-    while (retry-- > 0) {
-        sp<V1_0::ISensors> sensors = V1_0::ISensors::getService();
-        if (sensors == nullptr) {
-            // no sensor hidl service found
-            connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
-            break;
+            } else {
+                // Don't crash here or the device will go into a crashloop.
+                ALOGW("%s should have a non-zero resolution", sensor.name);
+            }
         }
 
-        mSensors = new ISensorsWrapperV1_0(sensors);
-        mRestartWaiter->reset();
-        // Poke ISensor service. If it has lingering connection from previous generation of
-        // system server, it will kill itself. There is no intention to handle the poll result,
-        // which will be done since the size is 0.
-        if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
-            // ok to continue
-            connectionStatus = HalConnectionStatus::CONNECTED;
-            break;
+        // Check and clamp power if it is 0 (or close)
+        constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
+        if (sensor.power < MIN_POWER_MA) {
+            ALOGI("%s's reported power %f invalid, clamped to %f", sensor.name, sensor.power,
+                  MIN_POWER_MA);
+            sensor.power = MIN_POWER_MA;
         }
+        mSensorList.push_back(sensor);
 
-        // hidl service is restarting, pointer is invalid.
-        mSensors = nullptr;
-        connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
-        ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
-        mRestartWaiter->wait();
+        mActivationCount.add(list[i].handle, model);
+
+        // Only disable all sensors on HAL 1.0 since HAL 2.0
+        // handles this in its initialize method
+        if (!mHalWrapper->supportsMessageQueues()) {
+            mHalWrapper->activate(list[i].handle, 0 /* enabled */);
+        }
     }
-
-    return connectionStatus;
 }
 
-SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_0() {
-    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
-    sp<V2_0::ISensors> sensors = V2_0::ISensors::getService();
+SensorDevice::~SensorDevice() {}
 
-    if (sensors == nullptr) {
-        connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
-    } else {
-        mSensors = new ISensorsWrapperV2_0(sensors);
-        connectionStatus = initializeHidlServiceV2_X();
+bool SensorDevice::connectHalService() {
+    std::unique_ptr<ISensorHalWrapper> aidl_wrapper = std::make_unique<AidlSensorHalWrapper>();
+    if (aidl_wrapper->connect(this)) {
+        mHalWrapper = std::move(aidl_wrapper);
+        return true;
     }
 
-    return connectionStatus;
-}
-
-SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_1() {
-    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
-    sp<V2_1::ISensors> sensors = V2_1::ISensors::getService();
-
-    if (sensors == nullptr) {
-        connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
-    } else {
-        mSensors = new ISensorsWrapperV2_1(sensors);
-        connectionStatus = initializeHidlServiceV2_X();
+    std::unique_ptr<ISensorHalWrapper> hidl_wrapper = std::make_unique<HidlSensorHalWrapper>();
+    if (hidl_wrapper->connect(this)) {
+        mHalWrapper = std::move(hidl_wrapper);
+        return true;
     }
 
-    return connectionStatus;
-}
-
-SensorDevice::HalConnectionStatus SensorDevice::initializeHidlServiceV2_X() {
-    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
-
-    mWakeLockQueue = std::make_unique<WakeLockQueue>(
-            SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
-            true /* configureEventFlagWord */);
-
-    hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
-    hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag);
-
-    hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
-    hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(),
-                                            &mWakeLockQueueFlag);
-
-    CHECK(mSensors != nullptr && mWakeLockQueue != nullptr &&
-            mEventQueueFlag != nullptr && mWakeLockQueueFlag != nullptr);
-
-    status_t status = checkReturnAndGetStatus(mSensors->initialize(
-            *mWakeLockQueue->getDesc(),
-            new SensorsCallback()));
-
-    if (status != NO_ERROR) {
-        connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
-        ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
-    } else {
-        connectionStatus = HalConnectionStatus::CONNECTED;
-        mSensorsHalDeathReceiver = new SensorsHalDeathReceivier();
-        mSensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
-    }
-
-    return connectionStatus;
+    // TODO: check aidl connection;
+    return false;
 }
 
 void SensorDevice::prepareForReconnect() {
-    mReconnecting = true;
-
-    // Wake up the polling thread so it returns and allows the SensorService to initiate
-    // a reconnect.
-    mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+    mHalWrapper->prepareForReconnect();
 }
 
 void SensorDevice::reconnect() {
     Mutex::Autolock _l(mLock);
-    mSensors = nullptr;
 
     auto previousActivations = mActivationCount;
     auto previousSensorList = mSensorList;
@@ -326,7 +166,7 @@
     mActivationCount.clear();
     mSensorList.clear();
 
-    if (connectHidlService()) {
+    if (mHalWrapper->connect(this)) {
         initializeSensorList();
 
         if (sensorHandlesChanged(previousSensorList, mSensorList)) {
@@ -335,11 +175,11 @@
             reactivateSensors(previousActivations);
         }
     }
-    mReconnecting = false;
+    mHalWrapper->mReconnecting = false;
 }
 
-bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
-                                        const Vector<sensor_t>& newSensorList) {
+bool SensorDevice::sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList,
+                                        const std::vector<sensor_t>& newSensorList) {
     bool didChange = false;
 
     if (oldSensorList.size() != newSensorList.size()) {
@@ -375,19 +215,17 @@
 bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) {
     bool equivalent = true;
     if (prevSensor.handle != newSensor.handle ||
-            (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
-            (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
-            (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
-            (prevSensor.version != newSensor.version) ||
-            (prevSensor.type != newSensor.type) ||
-            (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
-            (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
-            (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
-            (prevSensor.minDelay != newSensor.minDelay) ||
-            (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
-            (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
-            (prevSensor.maxDelay != newSensor.maxDelay) ||
-            (prevSensor.flags != newSensor.flags)) {
+        (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
+        (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
+        (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
+        (prevSensor.version != newSensor.version) || (prevSensor.type != newSensor.type) ||
+        (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
+        (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
+        (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
+        (prevSensor.minDelay != newSensor.minDelay) ||
+        (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
+        (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
+        (prevSensor.maxDelay != newSensor.maxDelay) || (prevSensor.flags != newSensor.flags)) {
         equivalent = false;
     }
     return equivalent;
@@ -405,7 +243,7 @@
         for (size_t j = 0; j < info.batchParams.size(); j++) {
             const BatchParams& batchParams = info.batchParams[j];
             status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */,
-                    batchParams.mTSample, batchParams.mTBatch);
+                                       batchParams.mTSample, batchParams.mTBatch);
 
             if (res == NO_ERROR) {
                 activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */);
@@ -419,21 +257,21 @@
     if (connected) {
         Info model;
         mActivationCount.add(handle, model);
-        checkReturn(mSensors->activate(handle, 0 /* enabled */));
+        mHalWrapper->activate(handle, 0 /* enabled */);
     } else {
         mActivationCount.removeItem(handle);
     }
 }
 
 std::string SensorDevice::dump() const {
-    if (mSensors == nullptr) return "HAL not initialized\n";
+    if (mHalWrapper == nullptr) return "HAL not initialized\n";
 
     String8 result;
     result.appendFormat("Total %zu h/w sensors, %zu running %zu disabled clients:\n",
                         mSensorList.size(), mActivationCount.size(), mDisabledClients.size());
 
     Mutex::Autolock _l(mLock);
-    for (const auto & s : mSensorList) {
+    for (const auto& s : mSensorList) {
         int32_t handle = s.handle;
         const Info& info = mActivationCount.valueFor(handle);
         if (info.numActiveClients() == 0) continue;
@@ -444,8 +282,9 @@
         for (size_t j = 0; j < info.batchParams.size(); j++) {
             const BatchParams& params = info.batchParams[j];
             result.appendFormat("%.1f%s%s", params.mTSample / 1e6f,
-                isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
-                (j < info.batchParams.size() - 1) ? ", " : "");
+                                isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)"
+                                                                                  : "",
+                                (j < info.batchParams.size() - 1) ? ", " : "");
         }
         result.appendFormat("}, selected = %.2f ms; ", info.bestBatchParams.mTSample / 1e6f);
 
@@ -453,8 +292,9 @@
         for (size_t j = 0; j < info.batchParams.size(); j++) {
             const BatchParams& params = info.batchParams[j];
             result.appendFormat("%.1f%s%s", params.mTBatch / 1e6f,
-                    isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
-                    (j < info.batchParams.size() - 1) ? ", " : "");
+                                isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)"
+                                                                                  : "",
+                                (j < info.batchParams.size() - 1) ? ", " : "");
         }
         result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f);
     }
@@ -471,30 +311,30 @@
  */
 void SensorDevice::dump(ProtoOutputStream* proto) const {
     using namespace service::SensorDeviceProto;
-    if (mSensors == nullptr) {
-        proto->write(INITIALIZED , false);
+    if (mHalWrapper == nullptr) {
+        proto->write(INITIALIZED, false);
         return;
     }
-    proto->write(INITIALIZED , true);
-    proto->write(TOTAL_SENSORS , int(mSensorList.size()));
-    proto->write(ACTIVE_SENSORS , int(mActivationCount.size()));
+    proto->write(INITIALIZED, true);
+    proto->write(TOTAL_SENSORS, int(mSensorList.size()));
+    proto->write(ACTIVE_SENSORS, int(mActivationCount.size()));
 
     Mutex::Autolock _l(mLock);
-    for (const auto & s : mSensorList) {
+    for (const auto& s : mSensorList) {
         int32_t handle = s.handle;
         const Info& info = mActivationCount.valueFor(handle);
         if (info.numActiveClients() == 0) continue;
 
         uint64_t token = proto->start(SENSORS);
-        proto->write(SensorProto::HANDLE , handle);
-        proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size()));
+        proto->write(SensorProto::HANDLE, handle);
+        proto->write(SensorProto::ACTIVE_COUNT, int(info.batchParams.size()));
         for (size_t j = 0; j < info.batchParams.size(); j++) {
             const BatchParams& params = info.batchParams[j];
-            proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f);
-            proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f);
+            proto->write(SensorProto::SAMPLING_PERIOD_MS, params.mTSample / 1e6f);
+            proto->write(SensorProto::BATCHING_PERIOD_MS, params.mTBatch / 1e6f);
         }
-        proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f);
-        proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f);
+        proto->write(SensorProto::SAMPLING_PERIOD_SELECTED, info.bestBatchParams.mTSample / 1e6f);
+        proto->write(SensorProto::BATCHING_PERIOD_SELECTED, info.bestBatchParams.mTBatch / 1e6f);
         proto->end(token);
     }
 }
@@ -506,154 +346,82 @@
 }
 
 status_t SensorDevice::initCheck() const {
-    return mSensors != nullptr ? NO_ERROR : NO_INIT;
+    return mHalWrapper != nullptr ? NO_ERROR : NO_INIT;
 }
 
 ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
-    if (mSensors == nullptr) return NO_INIT;
+    if (mHalWrapper == nullptr) return NO_INIT;
 
     ssize_t eventsRead = 0;
-    if (mSensors->supportsMessageQueues()) {
-        eventsRead = pollFmq(buffer, count);
-    } else if (mSensors->supportsPolling()) {
-        eventsRead = pollHal(buffer, count);
+    if (mHalWrapper->supportsMessageQueues()) {
+        eventsRead = mHalWrapper->pollFmq(buffer, count);
+    } else if (mHalWrapper->supportsPolling()) {
+        eventsRead = mHalWrapper->poll(buffer, count);
     } else {
         ALOGE("Must support polling or FMQ");
         eventsRead = -1;
     }
-    return eventsRead;
-}
 
-ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) {
-    ssize_t err;
-    int numHidlTransportErrors = 0;
-    bool hidlTransportError = false;
+    if (eventsRead > 0) {
+        for (ssize_t i = 0; i < eventsRead; i++) {
+            float resolution = getResolutionForSensor(buffer[i].sensor);
+            android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i], resolution);
 
-    do {
-        auto ret = mSensors->poll(
-                count,
-                [&](auto result,
-                    const auto &events,
-                    const auto &dynamicSensorsAdded) {
-                    if (result == Result::OK) {
-                        convertToSensorEventsAndQuantize(convertToNewEvents(events),
-                                convertToNewSensorInfos(dynamicSensorsAdded), buffer);
-                        err = (ssize_t)events.size();
-                    } else {
-                        err = statusFromResult(result);
+            if (buffer[i].type == SENSOR_TYPE_DYNAMIC_SENSOR_META) {
+                struct dynamic_sensor_meta_event& dyn = buffer[i].dynamic_sensor_meta;
+                if (dyn.connected) {
+                    std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
+                    // Give MAX_DYN_SENSOR_WAIT_SEC for onDynamicSensorsConnected to be invoked
+                    // since it can be received out of order from this event due to a bug in the
+                    // HIDL spec that marks it as oneway.
+                    auto it = mConnectedDynamicSensors.find(dyn.handle);
+                    if (it == mConnectedDynamicSensors.end()) {
+                        mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT, [&, dyn] {
+                            return mConnectedDynamicSensors.find(dyn.handle) !=
+                                    mConnectedDynamicSensors.end();
+                        });
+                        it = mConnectedDynamicSensors.find(dyn.handle);
+                        CHECK(it != mConnectedDynamicSensors.end());
                     }
-                });
 
-        if (ret.isOk())  {
-            hidlTransportError = false;
-        } else {
-            hidlTransportError = true;
-            numHidlTransportErrors++;
-            if (numHidlTransportErrors > 50) {
-                // Log error and bail
-                ALOGE("Max Hidl transport errors this cycle : %d", numHidlTransportErrors);
-                handleHidlDeath(ret.description());
-            } else {
-                std::this_thread::sleep_for(std::chrono::milliseconds(10));
+                    dyn.sensor = &it->second;
+                }
             }
         }
-    } while (hidlTransportError);
-
-    if(numHidlTransportErrors > 0) {
-        ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
-        HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors);
-        mHidlTransportErrors.add(errLog);
-        mTotalHidlTransportErrors++;
-    }
-
-    return err;
-}
-
-ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) {
-    ssize_t eventsRead = 0;
-    size_t availableEvents = mSensors->getEventQueue()->availableToRead();
-
-    if (availableEvents == 0) {
-        uint32_t eventFlagState = 0;
-
-        // Wait for events to become available. This is necessary so that the Event FMQ's read() is
-        // able to be called with the correct number of events to read. If the specified number of
-        // events is not available, then read() would return no events, possibly introducing
-        // additional latency in delivering events to applications.
-        mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
-                              asBaseType(INTERNAL_WAKE), &eventFlagState);
-        availableEvents = mSensors->getEventQueue()->availableToRead();
-
-        if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
-            ALOGD("Event FMQ internal wake, returning from poll with no events");
-            return DEAD_OBJECT;
-        }
-    }
-
-    size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
-    if (eventsToRead > 0) {
-        if (mSensors->getEventQueue()->read(mEventBuffer.data(), eventsToRead)) {
-            // Notify the Sensors HAL that sensor events have been read. This is required to support
-            // the use of writeBlocking by the Sensors HAL.
-            mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
-
-            for (size_t i = 0; i < eventsToRead; i++) {
-                convertToSensorEvent(mEventBuffer[i], &buffer[i]);
-                android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i],
-                        getResolutionForSensor(buffer[i].sensor));
-            }
-            eventsRead = eventsToRead;
-        } else {
-            ALOGW("Failed to read %zu events, currently %zu events available",
-                    eventsToRead, availableEvents);
-        }
     }
 
     return eventsRead;
 }
 
-Return<void> SensorDevice::onDynamicSensorsConnected(
-        const hidl_vec<SensorInfo> &dynamicSensorsAdded) {
+void SensorDevice::onDynamicSensorsConnected(const std::vector<sensor_t>& dynamicSensorsAdded) {
     std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
 
     // Allocate a sensor_t structure for each dynamic sensor added and insert
     // it into the dictionary of connected dynamic sensors keyed by handle.
     for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
-        const SensorInfo &info = dynamicSensorsAdded[i];
+        const sensor_t& sensor = dynamicSensorsAdded[i];
 
-        auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+        auto it = mConnectedDynamicSensors.find(sensor.handle);
         CHECK(it == mConnectedDynamicSensors.end());
 
-        sensor_t *sensor = new sensor_t();
-        convertToSensor(convertToOldSensorInfo(info), sensor);
-
-        mConnectedDynamicSensors.insert(
-                std::make_pair(sensor->handle, sensor));
+        mConnectedDynamicSensors.insert(std::make_pair(sensor.handle, sensor));
     }
 
     mDynamicSensorsCv.notify_all();
-
-    return Return<void>();
 }
 
-Return<void> SensorDevice::onDynamicSensorsDisconnected(
-        const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) {
-    (void) dynamicSensorHandlesRemoved;
+void SensorDevice::onDynamicSensorsDisconnected(
+        const std::vector<int32_t>& /* dynamicSensorHandlesRemoved */) {
     // TODO: Currently dynamic sensors do not seem to be removed
-    return Return<void>();
 }
 
 void SensorDevice::writeWakeLockHandled(uint32_t count) {
-    if (mSensors != nullptr && mSensors->supportsMessageQueues()) {
-        if (mWakeLockQueue->write(&count)) {
-            mWakeLockQueueFlag->wake(asBaseType(WakeLockQueueFlagBits::DATA_WRITTEN));
-        } else {
-            ALOGW("Failed to write wake lock handled");
-        }
+    if (mHalWrapper != nullptr && mHalWrapper->supportsMessageQueues()) {
+        mHalWrapper->writeWakeLockHandled(count);
     }
 }
 
-void SensorDevice::autoDisable(void *ident, int handle) {
+void SensorDevice::autoDisable(void* ident, int handle) {
     Mutex::Autolock _l(mLock);
     ssize_t activationIndex = mActivationCount.indexOfKey(handle);
     if (activationIndex < 0) {
@@ -668,7 +436,7 @@
 }
 
 status_t SensorDevice::activate(void* ident, int handle, int enabled) {
-    if (mSensors == nullptr) return NO_INIT;
+    if (mHalWrapper == nullptr) return NO_INIT;
 
     Mutex::Autolock _l(mLock);
     return activateLocked(ident, handle, enabled);
@@ -687,15 +455,15 @@
     Info& info(mActivationCount.editValueAt(activationIndex));
 
     ALOGD_IF(DEBUG_CONNECTIONS,
-             "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",
-             ident, handle, enabled, info.batchParams.size());
+             "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", ident,
+             handle, enabled, info.batchParams.size());
 
     if (enabled) {
         ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
 
         if (isClientDisabledLocked(ident)) {
-            ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
-                    ident, handle);
+            ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d", ident,
+                  handle);
             return NO_ERROR;
         }
 
@@ -714,7 +482,6 @@
         // dictionary.
         auto it = mConnectedDynamicSensors.find(handle);
         if (it != mConnectedDynamicSensors.end()) {
-            delete it->second;
             mConnectedDynamicSensors.erase(it);
         }
 
@@ -726,11 +493,10 @@
                 // Call batch for this sensor with the previously calculated best effort
                 // batch_rate and timeout. One of the apps has unregistered for sensor
                 // events, and the best effort batch parameters might have changed.
-                ALOGD_IF(DEBUG_CONNECTIONS,
-                         "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, handle,
-                         info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
-                checkReturn(mSensors->batch(
-                        handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
+                ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64,
+                         handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
+                mHalWrapper->batch(handle, info.bestBatchParams.mTSample,
+                                   info.bestBatchParams.mTBatch);
             }
         } else {
             // sensor wasn't enabled for this ident
@@ -761,19 +527,15 @@
 status_t SensorDevice::doActivateHardwareLocked(int handle, bool enabled) {
     ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
              enabled);
-    status_t err = checkReturnAndGetStatus(mSensors->activate(handle, enabled));
+    status_t err = mHalWrapper->activate(handle, enabled);
     ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
              strerror(-err));
     return err;
 }
 
-status_t SensorDevice::batch(
-        void* ident,
-        int handle,
-        int flags,
-        int64_t samplingPeriodNs,
-        int64_t maxBatchReportLatencyNs) {
-    if (mSensors == nullptr) return NO_INIT;
+status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+                             int64_t maxBatchReportLatencyNs) {
+    if (mHalWrapper == nullptr) return NO_INIT;
 
     if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
         samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
@@ -783,7 +545,8 @@
     }
 
     ALOGD_IF(DEBUG_CONNECTIONS,
-             "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
+             "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64
+             " timeout=%" PRId64,
              ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
 
     Mutex::Autolock _l(mLock);
@@ -807,25 +570,24 @@
         info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs);
     }
 
-    status_t err =  updateBatchParamsLocked(handle, info);
+    status_t err = updateBatchParamsLocked(handle, info);
     if (err != NO_ERROR) {
-        ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s",
-              mSensors.get(), handle, info.bestBatchParams.mTSample,
-              info.bestBatchParams.mTBatch, strerror(-err));
+        ALOGE("sensor batch failed 0x%08x %" PRId64 " %" PRId64 " err=%s", handle,
+              info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch, strerror(-err));
         info.removeBatchParamsForIdent(ident);
     }
 
     return err;
 }
 
-status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) {
+status_t SensorDevice::updateBatchParamsLocked(int handle, Info& info) {
     BatchParams prevBestBatchParams = info.bestBatchParams;
     // Find the minimum of all timeouts and batch_rates for this sensor.
     info.selectBatchParams();
 
     ALOGD_IF(DEBUG_CONNECTIONS,
-             "\t>>> curr_period=%" PRId64 " min_period=%" PRId64
-             " curr_timeout=%" PRId64 " min_timeout=%" PRId64,
+             "\t>>> curr_period=%" PRId64 " min_period=%" PRId64 " curr_timeout=%" PRId64
+             " min_timeout=%" PRId64,
              prevBestBatchParams.mTSample, info.bestBatchParams.mTSample,
              prevBestBatchParams.mTBatch, info.bestBatchParams.mTBatch);
 
@@ -834,8 +596,8 @@
     if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
                  info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
-        err = checkReturnAndGetStatus(mSensors->batch(
-                handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
+        err = mHalWrapper->batch(handle, info.bestBatchParams.mTSample,
+                                 info.bestBatchParams.mTBatch);
     }
 
     return err;
@@ -846,15 +608,13 @@
 }
 
 int SensorDevice::getHalDeviceVersion() const {
-    if (mSensors == nullptr) return -1;
+    if (mHalWrapper == nullptr) return -1;
     return SENSORS_DEVICE_API_VERSION_1_4;
 }
 
-status_t SensorDevice::flush(void* ident, int handle) {
-    if (mSensors == nullptr) return NO_INIT;
-    if (isClientDisabled(ident)) return INVALID_OPERATION;
-    ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
-    return checkReturnAndGetStatus(mSensors->flush(handle));
+status_t SensorDevice::flush(void* /*ident*/, int handle) {
+    if (mHalWrapper == nullptr) return NO_INIT;
+    return mHalWrapper->flush(handle);
 }
 
 bool SensorDevice::isClientDisabled(void* ident) const {
@@ -866,8 +626,8 @@
     return mDisabledClients.count(ident) > 0;
 }
 
-std::vector<void *> SensorDevice::getDisabledClientsLocked() const {
-    std::vector<void *> vec;
+std::vector<void*> SensorDevice::getDisabledClientsLocked() const {
+    std::vector<void*> vec;
     for (const auto& it : mDisabledClients) {
         vec.push_back(it.first);
     }
@@ -896,7 +656,7 @@
         addDisabledReasonForIdentLocked(ident, DisabledReason::DISABLED_REASON_UID_IDLE);
     }
 
-    for (size_t i = 0; i< mActivationCount.size(); ++i) {
+    for (size_t i = 0; i < mActivationCount.size(); ++i) {
         int handle = mActivationCount.keyAt(i);
         Info& info = mActivationCount.editValueAt(i);
 
@@ -905,8 +665,7 @@
             bool disable = info.numActiveClients() == 0 && info.isActive;
             bool enable = info.numActiveClients() > 0 && !info.isActive;
 
-            if ((enable || disable) &&
-                doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+            if ((enable || disable) && doActivateHardwareLocked(handle, enable) == NO_ERROR) {
                 info.isActive = enable;
             }
         }
@@ -938,29 +697,27 @@
 }
 
 void SensorDevice::enableAllSensors() {
-    if (mSensors == nullptr) return;
+    if (mHalWrapper == nullptr) return;
     Mutex::Autolock _l(mLock);
 
-    for (void *client : getDisabledClientsLocked()) {
-        removeDisabledReasonForIdentLocked(
-            client, DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
+    for (void* client : getDisabledClientsLocked()) {
+        removeDisabledReasonForIdentLocked(client,
+                                           DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
     }
 
-    for (size_t i = 0; i< mActivationCount.size(); ++i) {
+    for (size_t i = 0; i < mActivationCount.size(); ++i) {
         Info& info = mActivationCount.editValueAt(i);
         if (info.batchParams.isEmpty()) continue;
         info.selectBatchParams();
         const int sensor_handle = mActivationCount.keyAt(i);
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
-                   sensor_handle);
-        status_t err = checkReturnAndGetStatus(mSensors->batch(
-                sensor_handle,
-                info.bestBatchParams.mTSample,
-                info.bestBatchParams.mTBatch));
+                 sensor_handle);
+        status_t err = mHalWrapper->batch(sensor_handle, info.bestBatchParams.mTSample,
+                                          info.bestBatchParams.mTBatch);
         ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
 
         if (err == NO_ERROR) {
-            err = checkReturnAndGetStatus(mSensors->activate(sensor_handle, 1 /* enabled */));
+            err = mHalWrapper->activate(sensor_handle, 1 /* enabled */);
             ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
         }
 
@@ -971,138 +728,57 @@
 }
 
 void SensorDevice::disableAllSensors() {
-    if (mSensors == nullptr) return;
+    if (mHalWrapper == nullptr) return;
     Mutex::Autolock _l(mLock);
-    for (size_t i = 0; i< mActivationCount.size(); ++i) {
+    for (size_t i = 0; i < mActivationCount.size(); ++i) {
         Info& info = mActivationCount.editValueAt(i);
         // Check if this sensor has been activated previously and disable it.
         if (info.batchParams.size() > 0) {
-           const int sensor_handle = mActivationCount.keyAt(i);
-           ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
-                   sensor_handle);
-           checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */));
+            const int sensor_handle = mActivationCount.keyAt(i);
+            ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
+                     sensor_handle);
+            mHalWrapper->activate(sensor_handle, 0 /* enabled */);
 
-           // Add all the connections that were registered for this sensor to the disabled
-           // clients list.
-           for (size_t j = 0; j < info.batchParams.size(); ++j) {
-               addDisabledReasonForIdentLocked(
-                   info.batchParams.keyAt(j), DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
-               ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
-           }
+            // Add all the connections that were registered for this sensor to the disabled
+            // clients list.
+            for (size_t j = 0; j < info.batchParams.size(); ++j) {
+                addDisabledReasonForIdentLocked(info.batchParams.keyAt(j),
+                                                DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
+                ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
+            }
 
-           info.isActive = false;
+            info.isActive = false;
         }
     }
 }
 
-status_t SensorDevice::injectSensorData(
-        const sensors_event_t *injected_sensor_event) {
-    if (mSensors == nullptr) return NO_INIT;
-    ALOGD_IF(DEBUG_CONNECTIONS,
-            "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
-            injected_sensor_event->sensor,
-            injected_sensor_event->timestamp, injected_sensor_event->data[0],
-            injected_sensor_event->data[1], injected_sensor_event->data[2],
-            injected_sensor_event->data[3], injected_sensor_event->data[4],
-            injected_sensor_event->data[5]);
-
-    Event ev;
-    V2_1::implementation::convertFromSensorEvent(*injected_sensor_event, &ev);
-
-    return checkReturnAndGetStatus(mSensors->injectSensorData(ev));
+status_t SensorDevice::injectSensorData(const sensors_event_t* injected_sensor_event) {
+    if (mHalWrapper == nullptr) return NO_INIT;
+    return mHalWrapper->injectSensorData(injected_sensor_event);
 }
 
 status_t SensorDevice::setMode(uint32_t mode) {
-    if (mSensors == nullptr) return NO_INIT;
-    return checkReturnAndGetStatus(mSensors->setOperationMode(
-            static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+    if (mHalWrapper == nullptr) return NO_INIT;
+    return mHalWrapper->setOperationMode(static_cast<SensorService::Mode>(mode));
 }
 
 int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
-    if (mSensors == nullptr) return NO_INIT;
+    if (mHalWrapper == nullptr) return NO_INIT;
     Mutex::Autolock _l(mLock);
 
-    SharedMemType type;
-    switch (memory->type) {
-        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
-            type = SharedMemType::ASHMEM;
-            break;
-        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
-            type = SharedMemType::GRALLOC;
-            break;
-        default:
-            return BAD_VALUE;
-    }
-
-    SharedMemFormat format;
-    if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
-        return BAD_VALUE;
-    }
-    format = SharedMemFormat::SENSORS_EVENT;
-
-    SharedMemInfo mem = {
-        .type = type,
-        .format = format,
-        .size = static_cast<uint32_t>(memory->size),
-        .memoryHandle = memory->handle,
-    };
-
-    int32_t ret;
-    checkReturn(mSensors->registerDirectChannel(mem,
-            [&ret](auto result, auto channelHandle) {
-                if (result == Result::OK) {
-                    ret = channelHandle;
-                } else {
-                    ret = statusFromResult(result);
-                }
-            }));
-    return ret;
+    return mHalWrapper->registerDirectChannel(memory, nullptr);
 }
 
 void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
-    if (mSensors == nullptr) return;
-    Mutex::Autolock _l(mLock);
-    checkReturn(mSensors->unregisterDirectChannel(channelHandle));
+    mHalWrapper->unregisterDirectChannel(channelHandle);
 }
 
-int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
-        int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
-    if (mSensors == nullptr) return NO_INIT;
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+                                             const struct sensors_direct_cfg_t* config) {
+    if (mHalWrapper == nullptr) return NO_INIT;
     Mutex::Autolock _l(mLock);
 
-    RateLevel rate;
-    switch(config->rate_level) {
-        case SENSOR_DIRECT_RATE_STOP:
-            rate = RateLevel::STOP;
-            break;
-        case SENSOR_DIRECT_RATE_NORMAL:
-            rate = RateLevel::NORMAL;
-            break;
-        case SENSOR_DIRECT_RATE_FAST:
-            rate = RateLevel::FAST;
-            break;
-        case SENSOR_DIRECT_RATE_VERY_FAST:
-            rate = RateLevel::VERY_FAST;
-            break;
-        default:
-            return BAD_VALUE;
-    }
-
-    int32_t ret;
-    checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
-            [&ret, rate] (auto result, auto token) {
-                if (rate == RateLevel::STOP) {
-                    ret = statusFromResult(result);
-                } else {
-                    if (result == Result::OK) {
-                        ret = token;
-                    } else {
-                        ret = statusFromResult(result);
-                    }
-                }
-            }));
-
-    return ret;
+    return mHalWrapper->configureDirectChannel(sensorHandle, channelHandle, config);
 }
 
 // ---------------------------------------------------------------------------
@@ -1118,13 +794,12 @@
     return num;
 }
 
-status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int,
-                                                    int64_t samplingPeriodNs,
+status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int, int64_t samplingPeriodNs,
                                                     int64_t maxBatchReportLatencyNs) {
     ssize_t index = batchParams.indexOfKey(ident);
     if (index < 0) {
-        ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64
-              " timeout=%" PRId64 ") failed (%s)",
+        ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64
+              ") failed (%s)",
               ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index));
         return BAD_INDEX;
     }
@@ -1168,84 +843,20 @@
     return mIsDirectReportSupported;
 }
 
-void SensorDevice::convertToSensorEvent(
-        const Event &src, sensors_event_t *dst) {
-    V2_1::implementation::convertToSensorEvent(src, dst);
-
-    if (src.sensorType == V2_1::SensorType::DYNAMIC_SENSOR_META) {
-        const DynamicSensorInfo &dyn = src.u.dynamic;
-
-        dst->dynamic_sensor_meta.connected = dyn.connected;
-        dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
-        if (dyn.connected) {
-            std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
-            // Give MAX_DYN_SENSOR_WAIT_SEC for onDynamicSensorsConnected to be invoked since it
-            // can be received out of order from this event due to a bug in the HIDL spec that
-            // marks it as oneway.
-            auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
-            if (it == mConnectedDynamicSensors.end()) {
-                mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT,
-                        [&, dyn]{
-                            return mConnectedDynamicSensors.find(dyn.sensorHandle)
-                                    != mConnectedDynamicSensors.end();
-                });
-                it = mConnectedDynamicSensors.find(dyn.sensorHandle);
-                CHECK(it != mConnectedDynamicSensors.end());
-            }
-
-            dst->dynamic_sensor_meta.sensor = it->second;
-
-            memcpy(dst->dynamic_sensor_meta.uuid,
-                   dyn.uuid.data(),
-                   sizeof(dst->dynamic_sensor_meta.uuid));
-        }
-    }
-}
-
-void SensorDevice::convertToSensorEventsAndQuantize(
-        const hidl_vec<Event> &src,
-        const hidl_vec<SensorInfo> &dynamicSensorsAdded,
-        sensors_event_t *dst) {
-
-    if (dynamicSensorsAdded.size() > 0) {
-        onDynamicSensorsConnected(dynamicSensorsAdded);
-    }
-
-    for (size_t i = 0; i < src.size(); ++i) {
-        V2_1::implementation::convertToSensorEvent(src[i], &dst[i]);
-        android::SensorDeviceUtils::quantizeSensorEventValues(&dst[i],
-                getResolutionForSensor(dst[i].sensor));
-    }
-}
-
 float SensorDevice::getResolutionForSensor(int sensorHandle) {
     for (size_t i = 0; i < mSensorList.size(); i++) {
-      if (sensorHandle == mSensorList[i].handle) {
-        return mSensorList[i].resolution;
-      }
+        if (sensorHandle == mSensorList[i].handle) {
+            return mSensorList[i].resolution;
+        }
     }
 
     auto it = mConnectedDynamicSensors.find(sensorHandle);
     if (it != mConnectedDynamicSensors.end()) {
-      return it->second->resolution;
+        return it->second.resolution;
     }
 
     return 0;
 }
 
-void SensorDevice::handleHidlDeath(const std::string & detail) {
-    if (!mSensors->supportsMessageQueues()) {
-        // restart is the only option at present.
-        LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
-    } else {
-        ALOGD("ISensors HAL died, death recipient will attempt reconnect");
-    }
-}
-
-status_t SensorDevice::checkReturnAndGetStatus(const Return<Result>& ret) {
-    checkReturn(ret);
-    return (!ret.isOk()) ? DEAD_OBJECT : statusFromResult(ret);
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index bc8d20f..747a6b0 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -17,14 +17,16 @@
 #ifndef ANDROID_SENSOR_DEVICE_H
 #define ANDROID_SENSOR_DEVICE_H
 
+#include "HidlSensorHalWrapper.h"
+#include "ISensorHalWrapper.h"
+
+#include "ISensorsWrapper.h"
 #include "SensorDeviceUtils.h"
 #include "SensorService.h"
 #include "SensorServiceUtils.h"
-#include "ISensorsWrapper.h"
 
-#include <fmq/MessageQueue.h>
-#include <sensor/SensorEventQueue.h>
 #include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/KeyedVector.h>
@@ -32,9 +34,10 @@
 #include <utils/String8.h>
 #include <utils/Timers.h>
 
+#include <algorithm> //std::max std::min
 #include <string>
 #include <unordered_map>
-#include <algorithm> //std::max std::min
+#include <vector>
 
 #include "RingBuffer.h"
 
@@ -43,40 +46,11 @@
 namespace android {
 
 // ---------------------------------------------------------------------------
-class SensorsHalDeathReceivier : public android::hardware::hidl_death_recipient {
-    virtual void serviceDied(uint64_t cookie,
-                             const wp<::android::hidl::base::V1_0::IBase>& service) override;
-};
 
 class SensorDevice : public Singleton<SensorDevice>,
-                     public SensorServiceUtil::Dumpable {
+                     public SensorServiceUtil::Dumpable,
+                     public ISensorHalWrapper::SensorDeviceCallback {
 public:
-    class HidlTransportErrorLog {
-     public:
-
-        HidlTransportErrorLog() {
-            mTs = 0;
-            mCount = 0;
-        }
-
-        HidlTransportErrorLog(time_t ts, int count) {
-            mTs = ts;
-            mCount = count;
-        }
-
-        String8 toString() const {
-            String8 result;
-            struct tm *timeInfo = localtime(&mTs);
-            result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min,
-                                timeInfo->tm_sec, mCount);
-            return result;
-        }
-
-    private:
-        time_t mTs; // timestamp of the error
-        int mCount;   // number of transport errors observed
-    };
-
     ~SensorDevice();
     void prepareForReconnect();
     void reconnect();
@@ -99,29 +73,27 @@
     status_t setMode(uint32_t mode);
 
     bool isDirectReportSupported() const;
-    int32_t registerDirectChannel(const sensors_direct_mem_t *memory);
+    int32_t registerDirectChannel(const sensors_direct_mem_t* memory);
     void unregisterDirectChannel(int32_t channelHandle);
-    int32_t configureDirectChannel(int32_t sensorHandle,
-            int32_t channelHandle, const struct sensors_direct_cfg_t *config);
+    int32_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+                                   const struct sensors_direct_cfg_t* config);
 
     void disableAllSensors();
     void enableAllSensors();
-    void autoDisable(void *ident, int handle);
+    void autoDisable(void* ident, int handle);
 
-    status_t injectSensorData(const sensors_event_t *event);
-    void notifyConnectionDestroyed(void *ident);
+    status_t injectSensorData(const sensors_event_t* event);
+    void notifyConnectionDestroyed(void* ident);
 
-    using Result = ::android::hardware::sensors::V1_0::Result;
-    hardware::Return<void> onDynamicSensorsConnected(
-            const hardware::hidl_vec<hardware::sensors::V2_1::SensorInfo> &dynamicSensorsAdded);
-    hardware::Return<void> onDynamicSensorsDisconnected(
-            const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved);
+    // SensorDeviceCallback
+    virtual void onDynamicSensorsConnected(
+            const std::vector<sensor_t>& dynamicSensorsAdded) override;
+    virtual void onDynamicSensorsDisconnected(
+            const std::vector<int32_t>& dynamicSensorHandlesRemoved) override;
 
     void setUidStateForConnection(void* ident, SensorService::UidState state);
 
-    bool isReconnecting() const {
-        return mReconnecting;
-    }
+    bool isReconnecting() const { return mHalWrapper->mReconnecting; }
 
     bool isSensorActive(int handle) const;
 
@@ -132,12 +104,14 @@
     // Dumpable
     virtual std::string dump() const override;
     virtual void dump(util::ProtoOutputStream* proto) const override;
+
 private:
     friend class Singleton<SensorDevice>;
 
-    sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors;
-    Vector<sensor_t> mSensorList;
-    std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
+    std::unique_ptr<ISensorHalWrapper> mHalWrapper;
+
+    std::vector<sensor_t> mSensorList;
+    std::unordered_map<int32_t, sensor_t> mConnectedDynamicSensors;
 
     // A bug in the Sensors HIDL spec which marks onDynamicSensorsConnected as oneway causes dynamic
     // meta events and onDynamicSensorsConnected to be received out of order. This mutex + CV are
@@ -147,26 +121,26 @@
     std::condition_variable mDynamicSensorsCv;
     static constexpr std::chrono::seconds MAX_DYN_SENSOR_WAIT{5};
 
-    static const nsecs_t MINIMUM_EVENTS_PERIOD =   1000000; // 1000 Hz
-    mutable Mutex mLock; // protect mActivationCount[].batchParams
+    static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
+    mutable Mutex mLock;                                  // protect mActivationCount[].batchParams
     // fixed-size array after construction
 
     // Struct to store all the parameters(samplingPeriod, maxBatchReportLatency and flags) from
     // batch call. For continous mode clients, maxBatchReportLatency is set to zero.
     struct BatchParams {
-      nsecs_t mTSample, mTBatch;
-      BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {}
-      BatchParams(nsecs_t tSample, nsecs_t tBatch): mTSample(tSample), mTBatch(tBatch) {}
-      bool operator != (const BatchParams& other) {
-          return !(mTSample == other.mTSample && mTBatch == other.mTBatch);
-      }
-      // Merge another parameter with this one. The updated mTSample will be the min of the two.
-      // The update mTBatch will be the min of original mTBatch and the apparent batch period
-      // of the other. the apparent batch is the maximum of mTBatch and mTSample,
-      void merge(const BatchParams &other) {
-          mTSample = std::min(mTSample, other.mTSample);
-          mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample));
-      }
+        nsecs_t mTSample, mTBatch;
+        BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {}
+        BatchParams(nsecs_t tSample, nsecs_t tBatch) : mTSample(tSample), mTBatch(tBatch) {}
+        bool operator!=(const BatchParams& other) {
+            return !(mTSample == other.mTSample && mTBatch == other.mTBatch);
+        }
+        // Merge another parameter with this one. The updated mTSample will be the min of the two.
+        // The update mTBatch will be the min of original mTBatch and the apparent batch period
+        // of the other. the apparent batch is the maximum of mTBatch and mTSample,
+        void merge(const BatchParams& other) {
+            mTSample = std::min(mTSample, other.mTSample);
+            mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample));
+        }
     };
 
     // Store batch parameters in the KeyedVector and the optimal batch_rate and timeout in
@@ -205,8 +179,6 @@
     };
     DefaultKeyedVector<int, Info> mActivationCount;
 
-    // Keep track of any hidl transport failures
-    SensorServiceUtil::RingBuffer<HidlTransportErrorLog> mHidlTransportErrors;
     int mTotalHidlTransportErrors;
 
     /**
@@ -224,32 +196,19 @@
     static_assert(DisabledReason::DISABLED_REASON_MAX < sizeof(uint8_t) * CHAR_BIT);
 
     // Use this map to determine which client is activated or deactivated.
-    std::unordered_map<void *, uint8_t> mDisabledClients;
+    std::unordered_map<void*, uint8_t> mDisabledClients;
 
     void addDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
     void removeDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
 
     SensorDevice();
-    bool connectHidlService();
+    bool connectHalService();
     void initializeSensorList();
     void reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations);
-    static bool sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
-                                     const Vector<sensor_t>& newSensorList);
+    static bool sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList,
+                                     const std::vector<sensor_t>& newSensorList);
     static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor);
 
-    enum HalConnectionStatus {
-        CONNECTED, // Successfully connected to the HAL
-        DOES_NOT_EXIST, // Could not find the HAL
-        FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize
-        UNKNOWN,
-    };
-    HalConnectionStatus connectHidlServiceV1_0();
-    HalConnectionStatus connectHidlServiceV2_0();
-    HalConnectionStatus connectHidlServiceV2_1();
-    HalConnectionStatus initializeHidlServiceV2_X();
-
-    ssize_t pollHal(sensors_event_t* buffer, size_t count);
-    ssize_t pollFmq(sensors_event_t* buffer, size_t count);
     status_t activateLocked(void* ident, int handle, int enabled);
     status_t batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs,
                          int64_t maxBatchReportLatencyNs);
@@ -257,47 +216,15 @@
     status_t updateBatchParamsLocked(int handle, Info& info);
     status_t doActivateHardwareLocked(int handle, bool enable);
 
-    void handleHidlDeath(const std::string &detail);
-    template<typename T>
-    void checkReturn(const Return<T>& ret) {
-        if (!ret.isOk()) {
-            handleHidlDeath(ret.description());
-        }
-    }
-    status_t checkReturnAndGetStatus(const Return<Result>& ret);
-    //TODO(b/67425500): remove waiter after bug is resolved.
-    sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
-
     bool isClientDisabled(void* ident) const;
     bool isClientDisabledLocked(void* ident) const;
-    std::vector<void *> getDisabledClientsLocked() const;
+    std::vector<void*> getDisabledClientsLocked() const;
 
     bool clientHasNoAccessLocked(void* ident) const;
 
-    using Event = hardware::sensors::V2_1::Event;
-    using SensorInfo = hardware::sensors::V2_1::SensorInfo;
-
-    void convertToSensorEvent(const Event &src, sensors_event_t *dst);
-
-    void convertToSensorEventsAndQuantize(
-            const hardware::hidl_vec<Event> &src,
-            const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
-            sensors_event_t *dst);
-
     float getResolutionForSensor(int sensorHandle);
 
     bool mIsDirectReportSupported;
-
-    typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue;
-    std::unique_ptr<WakeLockQueue> mWakeLockQueue;
-
-    hardware::EventFlag* mEventQueueFlag;
-    hardware::EventFlag* mWakeLockQueueFlag;
-
-    std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
-
-    sp<SensorsHalDeathReceivier> mSensorsHalDeathReceiver;
-    std::atomic_bool mReconnecting;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index af86d09..fae16f6 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -32,7 +32,6 @@
         : mService(service), mUid(uid), mMem(*mem),
         mHalChannelHandle(halChannelHandle),
         mOpPackageName(opPackageName), mDestroyed(false) {
-    mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
     mUserId = multiuser_get_user_id(mUid);
     ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
 }
@@ -197,8 +196,8 @@
             if (mService->isSensorInCappedSet(s.getType())) {
                 // Back up the rates that the app is allowed to have if the mic toggle is off
                 // This is used in the uncapRates() function.
-                if (!mIsRateCappedBasedOnPermission ||
-                            requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
+                if ((requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) ||
+                    !isRateCappedBasedOnPermission()) {
                     mMicRateBackup[handle] = requestedRateLevel;
                 } else {
                     mMicRateBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index a3f348b..d39a073 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
 #define ANDROID_SENSOR_DIRECT_CONNECTION_H
 
+#include <optional>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -100,10 +101,19 @@
     std::unordered_map<int, int> mActivatedBackup;
     std::unordered_map<int, int> mMicRateBackup;
 
-    std::atomic_bool mIsRateCappedBasedOnPermission;
     mutable Mutex mDestroyLock;
     bool mDestroyed;
     userid_t mUserId;
+
+    std::optional<bool> mIsRateCappedBasedOnPermission;
+
+    bool isRateCappedBasedOnPermission() {
+      if (!mIsRateCappedBasedOnPermission.has_value()) {
+        mIsRateCappedBasedOnPermission =
+            mService->isRateCappedBasedOnPermission(mOpPackageName);
+      }
+      return mIsRateCappedBasedOnPermission.value();
+    }
 };
 
 } // namepsace android
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 9ce8d9b..6948895 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -44,7 +44,6 @@
       mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
       mPackageName(packageName), mOpPackageName(opPackageName), mAttributionTag(attributionTag),
       mTargetSdk(kTargetSdkUnknown), mDestroyed(false) {
-    mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
     mUserId = multiuser_get_user_id(mUid);
     mChannel = new BitTube(mService->mSocketBufferSize);
 #if DEBUG_CONNECTIONS
@@ -706,8 +705,8 @@
         err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,
                                reservedFlags, mOpPackageName);
         if (err == OK && isSensorCapped) {
-            if (!mIsRateCappedBasedOnPermission ||
-                        requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+            if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
+                !isRateCappedBasedOnPermission()) {
                 mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
             } else {
                 mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
@@ -745,8 +744,8 @@
     }
     status_t ret = mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
     if (ret == OK && isSensorCapped) {
-        if (!mIsRateCappedBasedOnPermission ||
-                    requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+        if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
+            !isRateCappedBasedOnPermission()) {
             mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
         } else {
             mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
@@ -867,7 +866,7 @@
             } else if (numBytesRead == sizeof(uint32_t)) {
                 uint32_t numAcks = 0;
                 memcpy(&numAcks, buf, numBytesRead);
-                // Sanity check to ensure  there are no read errors in recv, numAcks is always
+                // Check to ensure  there are no read errors in recv, numAcks is always
                 // within the range and not zero. If any of the above don't hold reset
                 // mWakeLockRefCount to zero.
                 if (numAcks > 0 && numAcks < mWakeLockRefCount) {
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 909053b..6a98a40 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -18,6 +18,7 @@
 #define ANDROID_SENSOR_EVENT_CONNECTION_H
 
 #include <atomic>
+#include <optional>
 #include <stdint.h>
 #include <sys/types.h>
 #include <unordered_map>
@@ -148,7 +149,6 @@
     sp<SensorService> const mService;
     sp<BitTube> mChannel;
     uid_t mUid;
-    std::atomic_bool mIsRateCappedBasedOnPermission;
     mutable Mutex mConnectionLock;
     // Number of events from wake up sensors which are still pending and haven't been delivered to
     // the corresponding application. It is incremented by one unit for each write to the socket.
@@ -201,6 +201,16 @@
     // Mapping of sensor handles to its rate before being capped by the mic toggle.
     std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup;
     userid_t mUserId;
+
+    std::optional<bool> mIsRateCappedBasedOnPermission;
+
+    bool isRateCappedBasedOnPermission() {
+      if (!mIsRateCappedBasedOnPermission.has_value()) {
+        mIsRateCappedBasedOnPermission
+            = mService->isRateCappedBasedOnPermission(mOpPackageName);
+      }
+      return mIsRateCappedBasedOnPermission.value();
+    }
 };
 
 } // namepsace android
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index bdbae7b..9bc7b8e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -164,7 +164,6 @@
         sensor_t const* list;
         ssize_t count = dev.getSensorList(&list);
         if (count > 0) {
-            ssize_t orientationIndex = -1;
             bool hasGyro = false, hasAccel = false, hasMag = false;
             uint32_t virtualSensorsNeeds =
                     (1<<SENSOR_TYPE_GRAVITY) |
@@ -183,9 +182,6 @@
                     case SENSOR_TYPE_MAGNETIC_FIELD:
                         hasMag = true;
                         break;
-                    case SENSOR_TYPE_ORIENTATION:
-                        orientationIndex = i;
-                        break;
                     case SENSOR_TYPE_GYROSCOPE:
                     case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
                         hasGyro = true;
@@ -201,6 +197,8 @@
                             virtualSensorsNeeds &= ~(1<<list[i].type);
                         }
                         break;
+                    default:
+                        break;
                 }
                 if (useThisSensor) {
                     if (list[i].type == SENSOR_TYPE_PROXIMITY) {
@@ -1194,7 +1192,7 @@
     // We have a dynamic sensor.
 
     if (!sHmacGlobalKeyIsValid) {
-        // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+        // Rather than risk exposing UUIDs, we slow down dynamic sensors.
         ALOGW("HMAC key failure; dynamic sensor getId() will be wrong.");
         return 0;
     }
@@ -1220,7 +1218,7 @@
              sHmacGlobalKey, sizeof(sHmacGlobalKey),
              uuidAndApp, sizeof(uuidAndApp),
              hash, &hashLen) == nullptr) {
-        // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+        // Rather than risk exposing UUIDs, we slow down dynamic sensors.
         ALOGW("HMAC failure; dynamic sensor getId() will be wrong.");
         return 0;
     }
@@ -2182,10 +2180,10 @@
 status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs,
         const String16& opPackageName) {
     uid_t uid = IPCThreadState::self()->getCallingUid();
-    bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
     if (*requestedPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
         return OK;
     }
+    bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
     if (shouldCapBasedOnPermission) {
         *requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
         if (isPackageDebuggable(opPackageName)) {
@@ -2203,11 +2201,10 @@
 status_t SensorService::adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel,
         const String16& opPackageName) {
     uid_t uid = IPCThreadState::self()->getCallingUid();
-    bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
-
     if (*requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
         return OK;
     }
+    bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
     if (shouldCapBasedOnPermission) {
         *requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
         if (isPackageDebuggable(opPackageName)) {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index b059e61..9b6d01a 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -89,6 +89,52 @@
       UID_STATE_IDLE,
     };
 
+    enum Mode {
+       // The regular operating mode where any application can register/unregister/call flush on
+       // sensors.
+       NORMAL = 0,
+       // This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
+       // the HAL ignores the sensor data provided by physical sensors and accepts the data that is
+       // injected from the SensorService as if it were the real sensor data. This mode is primarily
+       // used for testing various algorithms like vendor provided SensorFusion, Step Counter and
+       // Step Detector etc. Typically in this mode, there will be a client (a
+       // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can
+       // unregister and register for any sensor that supports injection. Registering to sensors
+       // that do not support injection will give an error.  TODO: Allow exactly one
+       // client to inject sensor data at a time.
+       DATA_INJECTION = 1,
+       // This mode is used only for testing sensors. Each sensor can be tested in isolation with
+       // the required sampling_rate and maxReportLatency parameters without having to think about
+       // the data rates requested by other applications. End user devices are always expected to be
+       // in NORMAL mode. When this mode is first activated, all active sensors from all connections
+       // are disabled. Calling flush() will return an error. In this mode, only the requests from
+       // selected apps whose package names are allowlisted are allowed (typically CTS apps).  Only
+       // these apps can register/unregister/call flush() on sensors. If SensorService switches to
+       // NORMAL mode again, all sensors that were previously registered to are activated with the
+       // corresponding parameters if the application hasn't unregistered for sensors in the mean
+       // time.  NOTE: Non allowlisted app whose sensors were previously deactivated may still
+       // receive events if a allowlisted app requests data from the same sensor.
+       RESTRICTED = 2
+
+      // State Transitions supported.
+      //     RESTRICTED   <---  NORMAL   ---> DATA_INJECTION
+      //                  --->           <---
+
+      // Shell commands to switch modes in SensorService.
+      // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in
+      // restricted mode it is treated as a NO_OP (and packageName is NOT changed).
+      //
+      //     $ adb shell dumpsys sensorservice restrict .cts.
+      //
+      // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in
+      // data_injection mode it is treated as a NO_OP (and packageName is NOT changed).
+      //
+      //     $ adb shell dumpsys sensorservice data_injection .xts.
+      //
+      // 3) Reset sensorservice back to NORMAL mode.
+      //     $ adb shell dumpsys sensorservice enable
+    };
+
     class ProximityActiveListener : public virtual RefBase {
     public:
         // Note that the callback is invoked from an async thread and can interact with the
@@ -276,52 +322,6 @@
             const int64_t mToken;
     };
 
-    enum Mode {
-       // The regular operating mode where any application can register/unregister/call flush on
-       // sensors.
-       NORMAL = 0,
-       // This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
-       // the HAL ignores the sensor data provided by physical sensors and accepts the data that is
-       // injected from the SensorService as if it were the real sensor data. This mode is primarily
-       // used for testing various algorithms like vendor provided SensorFusion, Step Counter and
-       // Step Detector etc. Typically in this mode, there will be a client (a
-       // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can
-       // unregister and register for any sensor that supports injection. Registering to sensors
-       // that do not support injection will give an error.  TODO(aakella) : Allow exactly one
-       // client to inject sensor data at a time.
-       DATA_INJECTION = 1,
-       // This mode is used only for testing sensors. Each sensor can be tested in isolation with
-       // the required sampling_rate and maxReportLatency parameters without having to think about
-       // the data rates requested by other applications. End user devices are always expected to be
-       // in NORMAL mode. When this mode is first activated, all active sensors from all connections
-       // are disabled. Calling flush() will return an error. In this mode, only the requests from
-       // selected apps whose package names are whitelisted are allowed (typically CTS apps).  Only
-       // these apps can register/unregister/call flush() on sensors. If SensorService switches to
-       // NORMAL mode again, all sensors that were previously registered to are activated with the
-       // corresponding paramaters if the application hasn't unregistered for sensors in the mean
-       // time.  NOTE: Non whitelisted app whose sensors were previously deactivated may still
-       // receive events if a whitelisted app requests data from the same sensor.
-       RESTRICTED = 2
-
-      // State Transitions supported.
-      //     RESTRICTED   <---  NORMAL   ---> DATA_INJECTION
-      //                  --->           <---
-
-      // Shell commands to switch modes in SensorService.
-      // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in
-      // restricted mode it is treated as a NO_OP (and packageName is NOT changed).
-      //
-      //     $ adb shell dumpsys sensorservice restrict .cts.
-      //
-      // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in
-      // data_injection mode it is treated as a NO_OP (and packageName is NOT changed).
-      //
-      //     $ adb shell dumpsys sensorservice data_injection .xts.
-      //
-      // 3) Reset sensorservice back to NORMAL mode.
-      //     $ adb shell dumpsys sensorservice enable
-    };
-
     static const char* WAKE_LOCK_NAME;
     virtual ~SensorService();
 
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9e69283..3e6d49f 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -39,15 +39,19 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
+        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
-        "android.hardware.power-V1-cpp",
+        "android.hardware.power-V2-cpp",
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libcutils",
         "libEGL",
         "libfmq",
@@ -66,13 +70,18 @@
         "libinput",
         "libutils",
         "libSurfaceFlingerProp",
+        "server_configurable_flags",
     ],
     static_libs: [
+        "libaidlcommonsupport",
         "libcompositionengine",
         "libframetimeline",
         "libperfetto_client_experimental",
         "librenderengine",
+        "libscheduler",
         "libserviceutils",
+        "libshaders",
+        "libtonemap",
         "libtrace_proto",
     ],
     header_libs: [
@@ -80,6 +89,7 @@
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
         "android.hardware.graphics.composer@2.4-command-buffer",
+        "android.hardware.graphics.composer3-command-buffer",
     ],
     export_static_lib_headers: [
         "libcompositionengine",
@@ -94,6 +104,7 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
+        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.power@1.3",
         "libhidlbase",
         "libtimestats",
@@ -131,6 +142,7 @@
 filegroup {
     name: "libsurfaceflinger_sources",
     srcs: [
+        "BackgroundExecutor.cpp",
         "BufferLayer.cpp",
         "BufferLayerConsumer.cpp",
         "BufferQueueLayer.cpp",
@@ -140,6 +152,8 @@
         "EffectLayer.cpp",
         "ContainerLayer.cpp",
         "DisplayDevice.cpp",
+        "DisplayHardware/AidlComposerHal.cpp",
+        "DisplayHardware/HidlComposerHal.cpp",
         "DisplayHardware/ComposerHal.cpp",
         "DisplayHardware/DisplayIdentification.cpp",
         "DisplayHardware/FramebufferSurface.cpp",
@@ -151,6 +165,7 @@
         "DisplayRenderArea.cpp",
         "Effects/Daltonizer.cpp",
         "EventLog/EventLog.cpp",
+        "FlagManager.cpp",
         "FpsReporter.cpp",
         "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
@@ -178,14 +193,17 @@
         "Scheduler/Timer.cpp",
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
-        "Scheduler/VsyncModulator.cpp",
         "Scheduler/VSyncReactor.cpp",
         "Scheduler/VsyncConfiguration.cpp",
+        "Scheduler/VsyncModulator.cpp",
+        "Scheduler/VsyncSchedule.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceFlingerDefaultFactory.cpp",
         "SurfaceInterceptor.cpp",
-        "SurfaceTracing.cpp",
+        "Tracing/LayerTracing.cpp",
+        "Tracing/TransactionTracing.cpp",
+        "Tracing/TransactionProtoParser.cpp",
         "TransactionCallbackInvoker.cpp",
         "TunnelModeEnabledReporter.cpp",
     ],
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
new file mode 100644
index 0000000..de8e6b3
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BackgroundExecutor"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BackgroundExecutor.h"
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BackgroundExecutor);
+
+BackgroundExecutor::BackgroundExecutor() : Singleton<BackgroundExecutor>() {
+    mThread = std::thread([&]() {
+        bool done = false;
+        while (!done) {
+            std::vector<std::function<void()>> tasks;
+            {
+                std::unique_lock lock(mMutex);
+                android::base::ScopedLockAssertion assumeLock(mMutex);
+                mWorkAvailableCv.wait(lock,
+                                      [&]() REQUIRES(mMutex) { return mDone || !mTasks.empty(); });
+                tasks = std::move(mTasks);
+                mTasks.clear();
+                done = mDone;
+            } // unlock mMutex
+
+            for (auto& task : tasks) {
+                task();
+            }
+        }
+    });
+}
+
+BackgroundExecutor::~BackgroundExecutor() {
+    {
+        std::scoped_lock lock(mMutex);
+        mDone = true;
+        mWorkAvailableCv.notify_all();
+    }
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void BackgroundExecutor::execute(std::function<void()> task) {
+    std::scoped_lock lock(mMutex);
+    mTasks.emplace_back(std::move(task));
+    mWorkAvailableCv.notify_all();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h
new file mode 100644
index 0000000..6db7dda
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <utils/Singleton.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+// Executes tasks off the main thread.
+class BackgroundExecutor : public Singleton<BackgroundExecutor> {
+public:
+    BackgroundExecutor();
+    ~BackgroundExecutor();
+    void execute(std::function<void()>);
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mWorkAvailableCv GUARDED_BY(mMutex);
+    bool mDone GUARDED_BY(mMutex) = false;
+    std::vector<std::function<void()>> mTasks GUARDED_BY(mMutex);
+    std::thread mThread;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 8c97f12..d61a4cb 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -111,6 +111,10 @@
     return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
 }
 
+bool BufferLayer::canReceiveInput() const {
+    return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
+}
+
 bool BufferLayer::isVisible() const {
     return !isHiddenByPolicy() && getAlpha() > 0.0f &&
             (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
@@ -152,39 +156,10 @@
         return result;
     }
 
-    if (CC_UNLIKELY(mBufferInfo.mBuffer == 0)) {
-        // the texture has not been created yet, this Layer has
-        // in fact never been drawn into. This happens frequently with
-        // SurfaceView because the WindowManager can't know when the client
-        // has drawn the first time.
-
-        // If there is nothing under us, we paint the screen in black, otherwise
-        // we just skip this update.
-
-        // figure out if there is something below us
-        Region under;
-        bool finished = false;
-        mFlinger->mDrawingState.traverseInZOrder([&](Layer* layer) {
-            if (finished || layer == static_cast<BufferLayer const*>(this)) {
-                finished = true;
-                return;
-            }
-
-            under.orSelf(layer->getScreenBounds());
-        });
-        // if not everything below us is covered, we plug the holes!
-        Region holes(targetSettings.clip.subtract(under));
-        if (!holes.isEmpty()) {
-            targetSettings.clearRegion.orSelf(holes);
-        }
-
-        if (mSidebandStream != nullptr) {
-            // For surfaceview of tv sideband, there is no activeBuffer
-            // in bufferqueue, we need return LayerSettings.
-            return result;
-        } else {
-            return std::nullopt;
-        }
+    if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) {
+        // For surfaceview of tv sideband, there is no activeBuffer
+        // in bufferqueue, we need return LayerSettings.
+        return result;
     }
     const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
             ((isSecure() || isProtected()) && !targetSettings.isSecure);
@@ -316,15 +291,19 @@
 
     // Sideband layers
     auto* compositionState = editCompositionState();
-    if (compositionState->sidebandStream.get()) {
-        compositionState->compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+    if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
+        compositionState->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
         return;
+    } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
+        compositionState->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
     } else {
         // Normal buffer layers
         compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
         compositionState->compositionType = mPotentialCursor
-                ? Hwc2::IComposerClient::Composition::CURSOR
-                : Hwc2::IComposerClient::Composition::DEVICE;
+                ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
+                : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
     }
 
     compositionState->buffer = mBufferInfo.mBuffer->getBuffer();
@@ -332,6 +311,7 @@
             ? 0
             : mBufferInfo.mBufferSlot;
     compositionState->acquireFence = mBufferInfo.mFence;
+    compositionState->sidebandStreamHasFrame = false;
 }
 
 bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -374,13 +354,13 @@
 }
 } // namespace
 
-bool BufferLayer::onPostComposition(const DisplayDevice* display,
+void BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
                                     const std::shared_ptr<FenceTime>& presentFence,
                                     const CompositorTiming& compositorTiming) {
     // mFrameLatencyNeeded is true when a new frame was latched for the
     // composition.
-    if (!mBufferInfo.mFrameLatencyNeeded) return false;
+    if (!mBufferInfo.mFrameLatencyNeeded) return;
 
     // Update mFrameEventHistory.
     {
@@ -425,12 +405,13 @@
         const Fps refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
         const std::optional<Fps> renderRate =
                 mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+
+        const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
+        const auto gameMode = getGameMode();
+
         if (presentFence->isValid()) {
             mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
-                                                  refreshRate, renderRate,
-                                                  frameRateToSetFrameRateVotePayload(
-                                                          mDrawingState.frameRate),
-                                                  getGameMode());
+                                                  refreshRate, renderRate, vote, gameMode);
             mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                presentFence,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -441,10 +422,7 @@
             // timestamp instead.
             const nsecs_t actualPresentTime = display->getRefreshTimestamp();
             mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
-                                                 refreshRate, renderRate,
-                                                 frameRateToSetFrameRateVotePayload(
-                                                         mDrawingState.frameRate),
-                                                 getGameMode());
+                                                 refreshRate, renderRate, vote, gameMode);
             mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
                                                    mCurrentFrameNumber, actualPresentTime,
                                                    FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -454,7 +432,6 @@
 
     mFrameTracker.advanceFrame();
     mBufferInfo.mFrameLatencyNeeded = false;
-    return true;
 }
 
 void BufferLayer::gatherBufferInfo() {
@@ -514,7 +491,7 @@
     // try again later
     if (!fenceHasSignaled()) {
         ATRACE_NAME("!fenceHasSignaled()");
-        mFlinger->signalLayerUpdate();
+        mFlinger->onLayerUpdate();
         return false;
     }
 
@@ -591,15 +568,16 @@
 // hardware.h, instead of using hard-coded values here.
 #define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
 
-bool BufferLayer::getOpacityForFormat(uint32_t format) {
+bool BufferLayer::getOpacityForFormat(PixelFormat format) {
     if (HARDWARE_IS_DEVICE_FORMAT(format)) {
         return true;
     }
     switch (format) {
-        case HAL_PIXEL_FORMAT_RGBA_8888:
-        case HAL_PIXEL_FORMAT_BGRA_8888:
-        case HAL_PIXEL_FORMAT_RGBA_FP16:
-        case HAL_PIXEL_FORMAT_RGBA_1010102:
+        case PIXEL_FORMAT_RGBA_8888:
+        case PIXEL_FORMAT_BGRA_8888:
+        case PIXEL_FORMAT_RGBA_FP16:
+        case PIXEL_FORMAT_RGBA_1010102:
+        case PIXEL_FORMAT_R_8:
             return false;
     }
     // in all other case, we have no blending (also for unknown formats)
@@ -648,14 +626,6 @@
     return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
 }
 
-uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const {
-    if (hasFrameUpdate()) {
-        return getFrameNumber(expectedPresentTime);
-    } else {
-        return mCurrentFrameNumber;
-    }
-}
-
 Rect BufferLayer::getBufferSize(const State& s) const {
     // If we have a sideband stream, or we are scaling the buffer then return the layer size since
     // we cannot determine the buffer size.
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 760c8b9..34d11ac 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -62,6 +62,7 @@
     void useEmptyDamage() override;
 
     bool isOpaque(const Layer::State& s) const override;
+    bool canReceiveInput() const override;
 
     // isVisible - true if this layer is visible, false otherwise
     bool isVisible() const override;
@@ -77,7 +78,7 @@
 
     bool isHdrY410() const override;
 
-    bool onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
+    void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
                            const std::shared_ptr<FenceTime>& presentFence,
                            const CompositorTiming&) override;
 
@@ -152,7 +153,7 @@
     bool onPreComposition(nsecs_t) override;
     void preparePerFrameCompositionState() override;
 
-    static bool getOpacityForFormat(uint32_t format);
+    static bool getOpacityForFormat(PixelFormat format);
 
     // from graphics API
     const uint32_t mTextureName;
@@ -164,15 +165,13 @@
     void updateCloneBufferInfo() override;
     uint64_t mPreviousFrameNumber = 0;
 
-    uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override;
-
     void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
 
     // Transform hint provided to the producer. This must be accessed holding
     // the mStateLock.
     ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
 
-    bool getAutoRefresh() const { return mAutoRefresh; }
+    bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
     bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
 
     // Returns true if the next buffer should be presented at the expected present time
@@ -183,14 +182,11 @@
     // specific logic
     virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
 
-    std::atomic<bool> mAutoRefresh{false};
     std::atomic<bool> mSidebandStreamChanged{false};
 
 private:
     virtual bool fenceHasSignaled() const = 0;
     virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
-    virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
-
 
     // Latch sideband stream and returns true if the dirty region should be updated.
     virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 96b2247..c79fa11 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -438,7 +438,7 @@
 }
 
 void BufferLayerConsumer::onSidebandStreamChanged() {
-    FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
+    [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
     {
         Mutex::Autolock lock(mFrameAvailableMutex);
         unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 99e470d..926aa1d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,7 +17,6 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "BufferQueueLayer"
@@ -49,7 +48,9 @@
 // Interface implementation for Layer
 // -----------------------------------------------------------------------
 
-void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+void BufferQueueLayer::onLayerDisplayed(
+        std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) {
+    sp<Fence> releaseFence = new Fence(dup(futureRenderEngineResult.get().drawFence));
     mConsumer->setReleaseFence(releaseFence);
 
     // Prevent tracing the same release multiple times.
@@ -66,16 +67,6 @@
     mConsumer->setTransformHint(mTransformHint);
 }
 
-std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) {
-    std::vector<OccupancyTracker::Segment> history;
-    status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
-    if (result != NO_ERROR) {
-        ALOGW("[%s] Failed to obtain occupancy history (%d)", getDebugName(), result);
-        return {};
-    }
-    return history;
-}
-
 void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     if (!mConsumer->releasePendingBuffer()) {
         return;
@@ -127,7 +118,7 @@
 bool BufferQueueLayer::fenceHasSignaled() const {
     Mutex::Autolock lock(mQueueItemLock);
 
-    if (SurfaceFlinger::enableLatchUnsignaled) {
+    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
         return true;
     }
 
@@ -153,54 +144,21 @@
 }
 
 bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
+    Mutex::Autolock lock(mQueueItemLock);
+
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
-    Mutex::Autolock lock(mQueueItemLock);
     return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
 }
 
-uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
-    Mutex::Autolock lock(mQueueItemLock);
-    uint64_t frameNumber = mQueueItems[0].item.mFrameNumber;
-
-    // The head of the queue will be dropped if there are signaled and timely frames behind it
-    if (isRemovedFromCurrentState()) {
-        expectedPresentTime = 0;
-    }
-
-    for (int i = 1; i < mQueueItems.size(); i++) {
-        const bool fenceSignaled =
-                mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
-        if (!fenceSignaled) {
-            break;
-        }
-
-        // We don't drop frames without explicit timestamps
-        if (mQueueItems[i].item.mIsAutoTimestamp) {
-            break;
-        }
-
-        const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp;
-        if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC ||
-            desiredPresent > expectedPresentTime) {
-            break;
-        }
-
-        frameNumber = mQueueItems[i].item.mFrameNumber;
-    }
-
-    return frameNumber;
-}
-
 bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
     // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
-    const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+    editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
 
     bool sidebandStreamChanged = true;
-    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) ||
-        updateSidebandStream) {
+    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
         // mSidebandStreamChanged was changed to false
         mSidebandStream = mConsumer->getSidebandStream();
         auto* layerCompositionState = editCompositionState();
@@ -243,7 +201,7 @@
     uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived;
     {
         Mutex::Autolock lock(mQueueItemLock);
-        for (int i = 0; i < mQueueItems.size(); i++) {
+        for (size_t i = 0; i < mQueueItems.size(); i++) {
             bool fenceSignaled =
                     mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
             if (!fenceSignaled) {
@@ -258,24 +216,26 @@
     bool autoRefresh;
     status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh,
                                                       &queuedBuffer, maxFrameNumberToAcquire);
-    mAutoRefresh = autoRefresh;
+    mDrawingState.autoRefresh = autoRefresh;
     if (updateResult == BufferQueue::PRESENT_LATER) {
         // Producer doesn't want buffer to be displayed yet.  Signal a
         // layer update so we check again at the next opportunity.
-        mFlinger->signalLayerUpdate();
+        mFlinger->onLayerUpdate();
         return BAD_VALUE;
     } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
         // If the buffer has been rejected, remove it from the shadow queue
         // and return early
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
-            mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
-            if (mQueueItems[0].surfaceFrame) {
-                addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+            if (mQueuedFrames > 0) {
+                mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+                mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+                if (mQueueItems[0].surfaceFrame) {
+                    addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+                }
+                mQueueItems.erase(mQueueItems.begin());
+                mQueuedFrames--;
             }
-            mQueueItems.erase(mQueueItems.begin());
-            mQueuedFrames--;
         }
         return BAD_VALUE;
     } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
@@ -305,6 +265,7 @@
         return BAD_VALUE;
     }
 
+    bool more_frames_pending = false;
     if (queuedBuffer) {
         // Autolock scope
         auto currentFrameNumber = mConsumer->getFrameNumber();
@@ -313,7 +274,7 @@
 
         // Remove any stale buffers that have been dropped during
         // updateTexImage
-        while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
+        while (mQueuedFrames > 0 && mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
             mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
             mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
             if (mQueueItems[0].surfaceFrame) {
@@ -334,12 +295,13 @@
                                               latchTime);
         }
         mQueueItems.erase(mQueueItems.begin());
+        more_frames_pending = (mQueuedFrames.fetch_sub(1) > 1);
     }
 
     // Decrement the queued-frames count.  Signal another event if we
     // have more frames pending.
-    if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) {
-        mFlinger->signalLayerUpdate();
+    if ((queuedBuffer && more_frames_pending) || mDrawingState.autoRefresh) {
+        mFlinger->onLayerUpdate();
     }
 
     return NO_ERROR;
@@ -410,8 +372,9 @@
     // Add this buffer from our internal queue tracker
     { // Autolock scope
         const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-        mFlinger->mScheduler->recordLayerHistory(this, presentTime,
-                                                 LayerHistory::LayerUpdateType::Buffer);
+
+        using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+        mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
 
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
@@ -442,7 +405,7 @@
     mFlinger->mInterceptor->saveBufferUpdate(layerId, item.mGraphicBuffer->getWidth(),
                                              item.mGraphicBuffer->getHeight(), item.mFrameNumber);
 
-    mFlinger->signalLayerUpdate();
+    mFlinger->onLayerUpdate();
     mConsumer->onBufferAvailable(item);
 }
 
@@ -488,7 +451,7 @@
     bool sidebandStreamChanged = false;
     if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) {
         // mSidebandStreamChanged was changed to true
-        mFlinger->signalLayerUpdate();
+        mFlinger->onLayerUpdate();
     }
 }
 
@@ -561,7 +524,7 @@
 }
 
 sp<Layer> BufferQueueLayer::createClone() {
-    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
     args.textureName = mTextureName;
     sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args);
     layer->setInitialValuesForClone(this);
@@ -625,4 +588,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index b3b7948..c6e0727 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -42,9 +42,8 @@
     // Implements Layer.
     const char* getType() const override { return "BufferQueueLayer"; }
 
-    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
-
-    std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
+    void onLayerDisplayed(
+            std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) override;
 
     // If a buffer was replaced this frame, release the former buffer
     void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
@@ -63,6 +62,11 @@
     status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
     sp<IGraphicBufferProducer> getProducer() const;
 
+    void setSizeForTest(uint32_t w, uint32_t h) {
+        mDrawingState.active_legacy.w = w;
+        mDrawingState.active_legacy.h = h;
+    }
+
 protected:
     void gatherBufferInfo() override;
 
@@ -89,7 +93,6 @@
     };
 
 private:
-    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool latchSidebandStream(bool& recomputeVisibleRegions) override;
     void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index df91904..2fac880 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "BufferStateLayer"
@@ -45,13 +40,13 @@
 namespace {
 void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
                                const sp<GraphicBuffer>& buffer, uint64_t framenumber,
-                               const sp<Fence>& releaseFence, uint32_t transformHint,
+                               const sp<Fence>& releaseFence,
                                uint32_t currentMaxAcquiredBufferCount) {
     if (!listener) {
         return;
     }
     listener->onReleaseBuffer({buffer->getId(), framenumber},
-                              releaseFence ? releaseFence : Fence::NO_FENCE, transformHint,
+                              releaseFence ? releaseFence : Fence::NO_FENCE,
                               currentMaxAcquiredBufferCount);
 }
 } // namespace
@@ -69,77 +64,30 @@
     if (mBufferInfo.mBuffer != nullptr) {
         callReleaseBufferCallback(mDrawingState.releaseBufferListener,
                                   mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
-                                  mBufferInfo.mFence, mTransformHint,
+                                  mBufferInfo.mFence,
                                   mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                           mOwnerUid));
     }
 }
 
-status_t BufferStateLayer::addReleaseFence(const sp<CallbackHandle>& ch,
-                                           const sp<Fence>& fence) {
-    if (ch == nullptr) {
-        return OK;
-    }
-    ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
-    if (!ch->previousReleaseFence.get()) {
-        ch->previousReleaseFence = fence;
-        return OK;
-    }
-
-    // Below logic is lifted from ConsumerBase.cpp:
-    // Check status of fences first because merging is expensive.
-    // Merging an invalid fence with any other fence results in an
-    // invalid fence.
-    auto currentStatus = ch->previousReleaseFence->getStatus();
-    if (currentStatus == Fence::Status::Invalid) {
-        ALOGE("Existing fence has invalid state, layer: %s", mName.c_str());
-        return BAD_VALUE;
-    }
-
-    auto incomingStatus = fence->getStatus();
-    if (incomingStatus == Fence::Status::Invalid) {
-        ALOGE("New fence has invalid state, layer: %s", mName.c_str());
-        ch->previousReleaseFence = fence;
-        return BAD_VALUE;
-    }
-
-    // If both fences are signaled or both are unsignaled, we need to merge
-    // them to get an accurate timestamp.
-    if (currentStatus == incomingStatus) {
-        char fenceName[32] = {};
-        snprintf(fenceName, 32, "%.28s", mName.c_str());
-        sp<Fence> mergedFence = Fence::merge(
-                fenceName, ch->previousReleaseFence, fence);
-        if (!mergedFence.get()) {
-            ALOGE("failed to merge release fences, layer: %s", mName.c_str());
-            // synchronization is broken, the best we can do is hope fences
-            // signal in order so the new fence will act like a union
-            ch->previousReleaseFence = fence;
-            return BAD_VALUE;
-        }
-        ch->previousReleaseFence = mergedFence;
-    } else if (incomingStatus == Fence::Status::Unsignaled) {
-        // If one fence has signaled and the other hasn't, the unsignaled
-        // fence will approximately correspond with the correct timestamp.
-        // There's a small race if both fences signal at about the same time
-        // and their statuses are retrieved with unfortunate timing. However,
-        // by this point, they will have both signaled and only the timestamp
-        // will be slightly off; any dependencies after this point will
-        // already have been met.
-        ch->previousReleaseFence = fence;
-    }
-    // else if (currentStatus == Fence::Status::Unsignaled) is a no-op.
-
-    return OK;
-}
-
 // -----------------------------------------------------------------------
 // Interface implementation for Layer
 // -----------------------------------------------------------------------
-void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
-    if (!releaseFence->isValid()) {
-        return;
+void BufferStateLayer::onLayerDisplayed(
+        std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) {
+    // If a layer has been displayed again we may need to clear
+    // the mLastClientComposition fence that we use for early release in setBuffer
+    // (as we now have a new fence which won't pass through the client composition path in some cases
+    //  e.g. screenshot). We expect one call to onLayerDisplayed after receiving the GL comp fence
+    // from a single composition cycle, and want to clear on the second call
+    // (which we track with mLastClientCompositionDisplayed)
+   if (mLastClientCompositionDisplayed) {
+        mLastClientCompositionFence = nullptr;
+        mLastClientCompositionDisplayed = false;
+    } else if (mLastClientCompositionFence) {
+        mLastClientCompositionDisplayed = true;
     }
+
     // The previous release fence notifies the client that SurfaceFlinger is done with the previous
     // buffer that was presented on this layer. The first transaction that came in this frame that
     // replaced the previous buffer on this layer needs this release fence, because the fence will
@@ -164,17 +112,17 @@
             break;
         }
     }
-    auto status = addReleaseFence(ch, releaseFence);
-    if (status != OK) {
-        ALOGE("Failed to add release fence for layer %s", getName().c_str());
-    }
-
-    mPreviousReleaseFence = releaseFence;
 
     // Prevent tracing the same release multiple times.
     if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
         mPreviousReleasedFrameNumber = mPreviousFrameNumber;
     }
+
+    if (ch != nullptr) {
+        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+        ch->previousReleaseFences.emplace_back(futureRenderEngineResult);
+        ch->name = mName;
+    }
 }
 
 void BufferStateLayer::onSurfaceFrameCreated(
@@ -219,12 +167,21 @@
                 JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
     }
 
-    mFlinger->getTransactionCallbackInvoker().finalizePendingCallbackHandles(
+    mFlinger->getTransactionCallbackInvoker().addCallbackHandles(
             mDrawingState.callbackHandles, jankData);
 
+    sp<Fence> releaseFence = Fence::NO_FENCE;
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer &&
+            mDrawingState.releaseBufferEndpoint == handle->listener) {
+            releaseFence =
+                    handle->previousReleaseFence ? handle->previousReleaseFence : Fence::NO_FENCE;
+            break;
+        }
+    }
+
     mDrawingState.callbackHandles = {};
 
-    const sp<Fence>& releaseFence(mPreviousReleaseFence);
     std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(releaseFence);
     {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -411,15 +368,55 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
-                                 const sp<Fence>& acquireFence, nsecs_t postTime,
+std::shared_ptr<renderengine::ExternalTexture> BufferStateLayer::getBufferFromBufferData(
+        const BufferData& bufferData) {
+    bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
+    bool bufferSizeExceedsLimit = false;
+    std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+    if (cacheIdChanged && bufferData.buffer != nullptr) {
+        bufferSizeExceedsLimit =
+                mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
+                                                     bufferData.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
+            buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+        }
+    } else if (cacheIdChanged) {
+        buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+    } else if (bufferData.buffer != nullptr) {
+        bufferSizeExceedsLimit =
+                mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
+                                                     bufferData.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            buffer = std::make_shared<
+                    renderengine::ExternalTexture>(bufferData.buffer, mFlinger->getRenderEngine(),
+                                                   renderengine::ExternalTexture::Usage::READABLE);
+        }
+    }
+    ALOGE_IF(bufferSizeExceedsLimit,
+             "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
+             "limit.",
+             getDebugName());
+    return buffer;
+}
+
+bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime,
                                  nsecs_t desiredPresentTime, bool isAutoTimestamp,
-                                 const client_cache_t& clientCacheId, uint64_t frameNumber,
-                                 std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
-                                 const sp<ITransactionCompletedListener>& releaseBufferListener,
-                                 const sp<IBinder>& releaseBufferEndpoint) {
+                                 std::optional<nsecs_t> dequeueTime,
+                                 const FrameTimelineInfo& info) {
     ATRACE_CALL();
 
+    const std::shared_ptr<renderengine::ExternalTexture>& buffer =
+            getBufferFromBufferData(bufferData);
+    if (!buffer) {
+        return false;
+    }
+
+    const bool frameNumberChanged =
+            bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
+    const uint64_t frameNumber =
+            frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
+
     if (mDrawingState.buffer) {
         mReleasePreviousBuffer = true;
         if (mDrawingState.buffer != mBufferInfo.mBuffer ||
@@ -430,7 +427,7 @@
             // call any release buffer callbacks if set.
             callReleaseBufferCallback(mDrawingState.releaseBufferListener,
                                       mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mDrawingState.acquireFence, mTransformHint,
+                                      mDrawingState.acquireFence,
                                       mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                               mOwnerUid));
             decrementPendingBufferCount();
@@ -439,13 +436,28 @@
               addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
               mDrawingState.bufferSurfaceFrameTX.reset();
             }
+        } else if (mLastClientCompositionFence != nullptr) {
+            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                      mLastClientCompositionFence,
+                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+                                              mOwnerUid));
+            mLastClientCompositionFence = nullptr;
         }
     }
 
     mDrawingState.frameNumber = frameNumber;
-    mDrawingState.releaseBufferListener = releaseBufferListener;
+    mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
     mDrawingState.buffer = buffer;
-    mDrawingState.clientCacheId = clientCacheId;
+    mDrawingState.clientCacheId = bufferData.cachedBuffer;
+
+    mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
+            ? bufferData.acquireFence
+            : Fence::NO_FENCE;
+    mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
+    // The acquire fences of BufferStateLayers have already signaled before they are set
+    mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime();
+
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
@@ -464,10 +476,11 @@
 
         return static_cast<nsecs_t>(0);
     }();
-    mFlinger->mScheduler->recordLayerHistory(this, presentTime,
-                                             LayerHistory::LayerUpdateType::Buffer);
 
-    addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
+    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+    mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
+
+    addFrameEvent(mDrawingState.acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
 
     setFrameTimelineVsyncForBufferTransaction(info, postTime);
 
@@ -482,20 +495,7 @@
 
     mDrawingState.width = mDrawingState.buffer->getBuffer()->getWidth();
     mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight();
-    mDrawingState.releaseBufferEndpoint = releaseBufferEndpoint;
-
-    return true;
-}
-
-bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
-    mDrawingState.acquireFence = fence;
-    mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(fence);
-
-    // The acquire fences of BufferStateLayers have already signaled before they are set
-    mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime();
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
+    mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
     return true;
 }
 
@@ -544,7 +544,7 @@
     setTransactionFlags(eTransactionNeeded);
     if (!mSidebandStreamChanged.exchange(true)) {
         // mSidebandStreamChanged was false
-        mFlinger->signalLayerUpdate();
+        mFlinger->onLayerUpdate();
     }
     return true;
 }
@@ -569,10 +569,6 @@
             handle->acquireTime = mCallbackHandleAcquireTime;
             handle->frameNumber = mDrawingState.frameNumber;
 
-            // Notify the transaction completed thread that there is a pending latched callback
-            // handle
-            mFlinger->getTransactionCallbackInvoker().registerPendingCallbackHandle(handle);
-
             // Store so latched time and release fence can be set
             mDrawingState.callbackHandles.push_back(handle);
 
@@ -596,7 +592,7 @@
     return true;
 }
 
-Rect BufferStateLayer::getBufferSize(const State& s) const {
+Rect BufferStateLayer::getBufferSize(const State& /*s*/) const {
     // for buffer state layers we use the display frame size as the buffer size.
 
     if (mBufferInfo.mBuffer == nullptr) {
@@ -618,7 +614,7 @@
         }
     }
 
-    return Rect(0, 0, bufWidth, bufHeight);
+    return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
 }
 
 FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
@@ -635,7 +631,7 @@
 // Interface implementation for BufferLayer
 // -----------------------------------------------------------------------
 bool BufferStateLayer::fenceHasSignaled() const {
-    if (SurfaceFlinger::enableLatchUnsignaled) {
+    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
         return true;
     }
 
@@ -664,47 +660,15 @@
     return BufferLayer::onPreComposition(refreshStartTime);
 }
 
-uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
-    return mDrawingState.frameNumber;
-}
-
-/**
- * This is the frameNumber used for deferred transaction signalling. We need to use this because
- * of cases where we defer a transaction for a surface to itself. In the BLAST world this
- * may not make a huge amount of sense (Why not just merge the Buffer transaction with the
- * deferred transaction?) but this is an important legacy use case, for example moving
- * a window at the same time it draws makes use of this kind of technique. So anyway
- * imagine we have something like this:
- *
- * Transaction { // containing
- *     Buffer -> frameNumber = 2
- *     DeferTransactionUntil -> frameNumber = 2
- *     Random other stuff
- *  }
- * Now imagine mFrameNumber returned mDrawingState.frameNumber (or mCurrentFrameNumber).
- * Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we
- * haven't swapped mDrawingState to mDrawingState yet we will think the sync point
- * is not ready. So we will return false from applyPendingState and not swap
- * current state to drawing state. But because we don't swap current state
- * to drawing state the number will never update and we will be stuck. This way
- * we can see we need to return the frame number for the buffer we are about
- * to apply.
- */
-uint64_t BufferStateLayer::getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const {
-    return mDrawingState.frameNumber;
-}
-
 void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
-    if (!mAutoRefresh.exchange(autoRefresh)) {
-        mFlinger->signalLayerUpdate();
-    }
+    mDrawingState.autoRefresh = autoRefresh;
 }
 
 bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
     // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
-    const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+    editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
 
-    if (mSidebandStreamChanged.exchange(false) || updateSidebandStream) {
+    if (mSidebandStreamChanged.exchange(false)) {
         const State& s(getDrawingState());
         // mSidebandStreamChanged was true
         mSidebandStream = s.sidebandStream;
@@ -770,7 +734,7 @@
 
     std::deque<sp<CallbackHandle>> remainingHandles;
     mFlinger->getTransactionCallbackInvoker()
-            .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+            .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
     mDrawingState.callbackHandles = remainingHandles;
 
     mDrawingStateModified = false;
@@ -817,7 +781,7 @@
     eraseBufferLocked(clientCacheId);
 }
 
-uint32_t BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
+int BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
     std::lock_guard<std::mutex> lock(mMutex);
     auto itr = mCachedBuffers.find(clientCacheId);
     if (itr == mCachedBuffers.end()) {
@@ -828,7 +792,7 @@
     return hwcCacheSlot;
 }
 
-uint32_t BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
+int BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
         REQUIRES(mMutex) {
     if (!clientCacheId.isValid()) {
         ALOGE("invalid process, returning invalid slot");
@@ -837,17 +801,17 @@
 
     ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
 
-    uint32_t hwcCacheSlot = getFreeHwcCacheSlot();
+    int hwcCacheSlot = getFreeHwcCacheSlot();
     mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
     return hwcCacheSlot;
 }
 
-uint32_t BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
+int BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
     if (mFreeHwcCacheSlots.empty()) {
         evictLeastRecentlyUsed();
     }
 
-    uint32_t hwcCacheSlot = mFreeHwcCacheSlots.top();
+    int hwcCacheSlot = mFreeHwcCacheSlots.top();
     mFreeHwcCacheSlots.pop();
     return hwcCacheSlot;
 }
@@ -920,7 +884,7 @@
 }
 
 sp<Layer> BufferStateLayer::createClone() {
-    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
     args.textureName = mTextureName;
     sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
     layer->mHwcSlotGenerator = mHwcSlotGenerator;
@@ -934,8 +898,8 @@
         return false;
     }
 
-    uint32_t bufferWidth = s.buffer->getBuffer()->width;
-    uint32_t bufferHeight = s.buffer->getBuffer()->height;
+    int32_t bufferWidth = s.buffer->getBuffer()->width;
+    int32_t bufferHeight = s.buffer->getBuffer()->height;
 
     // Undo any transformations on the buffer and return the result.
     if (s.bufferTransform & ui::Transform::ROT_90) {
@@ -994,6 +958,3 @@
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 0a0527c..eea700c 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -39,7 +39,9 @@
     // Implements Layer.
     const char* getType() const override { return "BufferStateLayer"; }
 
-    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+    void onLayerDisplayed(
+            std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) override;
+
     void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
 
     void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
@@ -55,13 +57,9 @@
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
-    bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
-                   const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime,
-                   bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber,
-                   std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
-                   const sp<ITransactionCompletedListener>& transactionListener,
-                   const sp<IBinder>& releaseBufferEndpoint) override;
-    bool setAcquireFence(const sp<Fence>& fence) override;
+    bool setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
+                   bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
+                   const FrameTimelineInfo& info) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
     bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
@@ -105,7 +103,6 @@
 
 protected:
     void gatherBufferInfo() override;
-    uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
     void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
     ui::Transform getInputTransform() const override;
     Rect getInputBounds() const override;
@@ -120,10 +117,6 @@
     bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
                                  nsecs_t requestedPresentTime);
 
-    status_t addReleaseFence(const sp<CallbackHandle>& ch, const sp<Fence>& releaseFence);
-
-    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
-
     bool latchSidebandStream(bool& recomputeVisibleRegions) override;
 
     bool hasFrameUpdate() const override;
@@ -143,7 +136,9 @@
 
     bool bufferNeedsFiltering() const override;
 
-    sp<Fence> mPreviousReleaseFence;
+    std::shared_ptr<renderengine::ExternalTexture> getBufferFromBufferData(
+            const BufferData& bufferData);
+
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
@@ -178,19 +173,19 @@
     class HwcSlotGenerator : public ClientCache::ErasedRecipient {
     public:
         HwcSlotGenerator() {
-            for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+            for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
                 mFreeHwcCacheSlots.push(i);
             }
         }
 
         void bufferErased(const client_cache_t& clientCacheId);
 
-        uint32_t getHwcCacheSlot(const client_cache_t& clientCacheId);
+        int getHwcCacheSlot(const client_cache_t& clientCacheId);
 
     private:
         friend class SlotGenerationTest;
-        uint32_t addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-        uint32_t getFreeHwcCacheSlot() REQUIRES(mMutex);
+        int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+        int getFreeHwcCacheSlot() REQUIRES(mMutex);
         void evictLeastRecentlyUsed() REQUIRES(mMutex);
         void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
 
@@ -202,11 +197,10 @@
 
         std::mutex mMutex;
 
-        std::unordered_map<client_cache_t,
-                           std::pair<uint32_t /*HwcCacheSlot*/, uint32_t /*counter*/>,
+        std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
                            CachedBufferHash>
                 mCachedBuffers GUARDED_BY(mMutex);
-        std::stack<uint32_t /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
+        std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
 
         // The cache increments this counter value when a slot is updated or used.
         // Used to track the least recently-used buffer
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index aac6c91..6d7b732 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -76,40 +72,34 @@
     return lbc;
 }
 
-status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
-                               uint32_t flags, const sp<IBinder>& parentHandle,
-                               LayerMetadata metadata, sp<IBinder>* handle,
-                               sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
-                               uint32_t* outTransformHint) {
+status_t Client::createSurface(const String8& name, uint32_t /* w */, uint32_t /* h */,
+                               PixelFormat /* format */, uint32_t flags,
+                               const sp<IBinder>& parentHandle, LayerMetadata metadata,
+                               sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* /* gbp */,
+                               int32_t* outLayerId, uint32_t* outTransformHint) {
     // We rely on createLayer to check permissions.
-    return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 parentHandle, outLayerId, nullptr, outTransformHint);
+    LayerCreationArgs args(mFlinger.get(), this, name.c_str(), flags, std::move(metadata));
+    return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, nullptr,
+                                 outTransformHint);
 }
 
-status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
-                                         PixelFormat format, uint32_t flags,
-                                         const sp<IGraphicBufferProducer>& parent,
-                                         LayerMetadata metadata, sp<IBinder>* handle,
-                                         sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
-                                         uint32_t* outTransformHint) {
-    if (mFlinger->authenticateSurfaceTexture(parent) == false) {
-        ALOGE("failed to authenticate surface texture");
-        return BAD_VALUE;
-    }
-
-    const auto& layer = (static_cast<MonitoredProducer*>(parent.get()))->getLayer();
-    if (layer == nullptr) {
-        ALOGE("failed to find parent layer");
-        return BAD_VALUE;
-    }
-
-    return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 nullptr, outLayerId, layer, outTransformHint);
+status_t Client::createWithSurfaceParent(const String8& /* name */, uint32_t /* w */,
+                                         uint32_t /* h */, PixelFormat /* format */,
+                                         uint32_t /* flags */,
+                                         const sp<IGraphicBufferProducer>& /* parent */,
+                                         LayerMetadata /* metadata */, sp<IBinder>* /* handle */,
+                                         sp<IGraphicBufferProducer>* /* gbp */,
+                                         int32_t* /* outLayerId */,
+                                         uint32_t* /* outTransformHint */) {
+    // This api does not make sense with blast since SF no longer tracks IGBP. This api should be
+    // removed.
+    return BAD_VALUE;
 }
 
 status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
                                int32_t* outLayerId) {
-    return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId);
+    LayerCreationArgs args(mFlinger.get(), this, "MirrorRoot", 0 /* flags */, LayerMetadata());
+    return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
 }
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
@@ -132,6 +122,3 @@
 
 // ---------------------------------------------------------------------------
 }; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index f310738..e7b8995 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -21,12 +21,12 @@
 
 #include <cinttypes>
 
+#include <android-base/stringprintf.h>
+
 #include "ClientCache.h"
 
 namespace android {
 
-using base::StringAppendF;
-
 ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
 
 ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
@@ -82,10 +82,13 @@
             return false;
         }
 
-        status_t err = token->linkToDeath(mDeathRecipient);
-        if (err != NO_ERROR) {
-            ALOGE("failed to cache buffer: could not link to death");
-            return false;
+        // Only call linkToDeath if not a local binder
+        if (token->localBinder() == nullptr) {
+            status_t err = token->linkToDeath(mDeathRecipient);
+            if (err != NO_ERROR) {
+                ALOGE("failed to cache buffer: could not link to death");
+                return false;
+            }
         }
         auto [itr, success] =
                 mBuffers.emplace(processToken,
@@ -212,16 +215,15 @@
 
 void ClientCache::dump(std::string& result) {
     std::lock_guard lock(mMutex);
-    for (auto i : mBuffers) {
-        const sp<IBinder>& cacheOwner = i.second.first;
-        StringAppendF(&result," Cache owner: %p\n", cacheOwner.get());
-        auto &buffers = i.second.second;
-        for (auto& [id, clientCacheBuffer] : buffers) {
-            StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id,
-                          (int)clientCacheBuffer.buffer->getBuffer()->getWidth(),
-                          (int)clientCacheBuffer.buffer->getBuffer()->getHeight());
+    for (const auto& [_, cache] : mBuffers) {
+        base::StringAppendF(&result, " Cache owner: %p\n", cache.first.get());
+
+        for (const auto& [id, entry] : cache.second) {
+            const auto& buffer = entry.buffer->getBuffer();
+            base::StringAppendF(&result, "\tID: %" PRIu64 ", size: %ux%u\n", id, buffer->getWidth(),
+                                buffer->getHeight());
         }
     }
 }
 
-}; // namespace android
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index d738ccd..aefc014 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -20,6 +20,7 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
+        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
         "libbase",
@@ -37,13 +38,16 @@
     static_libs: [
         "libmath",
         "librenderengine",
+        "libtonemap",
         "libtrace_proto",
+        "libaidlcommonsupport",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
         "android.hardware.graphics.composer@2.4-command-buffer",
+        "android.hardware.graphics.composer3-command-buffer",
         "libsurfaceflinger_headers",
     ],
 }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 554e2f4..f201751 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -47,9 +47,6 @@
     // All the layers that have queued updates.
     Layers layersWithQueuedFrames;
 
-    // If true, forces the entire display to be considered dirty and repainted
-    bool repaintEverything{false};
-
     // Controls how the color mode is chosen for an output
     OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
 
@@ -88,8 +85,11 @@
     // to prevent an early presentation of a frame.
     std::shared_ptr<FenceTime> previousPresentFence;
 
-    // The predicted next invalidation time
-    std::optional<std::chrono::steady_clock::time_point> nextInvalidateTime;
+    // The expected time for the next present
+    nsecs_t expectedPresentTime{0};
+
+    // If set, a frame has been scheduled for that time.
+    std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 14eddb1..98c4af4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -36,18 +36,12 @@
 struct DisplayCreationArgs {
     DisplayId id;
 
-    // Unset for virtual displays
-    std::optional<ui::DisplayConnectionType> connectionType;
-
     // Size of the display in pixels
-    ui::Size pixels = ui::Size::INVALID;
+    ui::Size pixels = ui::kInvalidSize;
 
     // True if this display should be considered secure
     bool isSecure = false;
 
-    // Gives the initial layer stack id to be used for the display
-    uint32_t layerStackId = ~0u;
-
     // Optional pointer to the power advisor interface, if one is needed for
     // this display.
     Hwc2::PowerAdvisor* powerAdvisor = nullptr;
@@ -69,11 +63,6 @@
         return *this;
     }
 
-    DisplayCreationArgsBuilder& setConnectionType(ui::DisplayConnectionType connectionType) {
-        mArgs.connectionType = connectionType;
-        return *this;
-    }
-
     DisplayCreationArgsBuilder& setPixels(ui::Size pixels) {
         mArgs.pixels = pixels;
         return *this;
@@ -84,11 +73,6 @@
         return *this;
     }
 
-    DisplayCreationArgsBuilder& setLayerStackId(uint32_t layerStackId) {
-        mArgs.layerStackId = layerStackId;
-        return *this;
-    }
-
     DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
         mArgs.powerAdvisor = powerAdvisor;
         return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 4502eee..c553fce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <cinttypes>
+
 #include <ui/Size.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
@@ -47,13 +49,8 @@
     // before composition takes place. The DisplaySurface can use the
     // composition type to decide how to manage the flow of buffers between
     // GPU and HWC for this frame.
-    enum CompositionType {
-        COMPOSITION_UNKNOWN = 0,
-        COMPOSITION_GPU = 1,
-        COMPOSITION_HWC = 2,
-        COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC
-    };
-    virtual status_t prepareFrame(CompositionType compositionType) = 0;
+    enum class CompositionType : uint8_t { Unknown = 0, Gpu = 0b1, Hwc = 0b10, Mixed = Gpu | Hwc };
+    virtual status_t prepareFrame(CompositionType) = 0;
 
     // Inform the surface that GPU composition is complete for this frame, and
     // the surface should make sure that HWComposer has the correct buffer for
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index e51019a..e77e155 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <future>
 #include <optional>
 #include <ostream>
 #include <unordered_set>
@@ -26,6 +27,7 @@
 #pragma clang diagnostic ignored "-Wextra"
 
 #include <renderengine/LayerSettings.h>
+#include <renderengine/RenderEngine.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -115,10 +117,6 @@
         // If set to true, the target buffer has protected content support.
         const bool supportsProtectedContent;
 
-        // Modified by each call to prepareClientComposition to indicate the
-        // region of the target buffer that should be cleared.
-        Region& clearRegion;
-
         // Viewport of the target being rendered to. This is used to determine
         // the shadow light position.
         const Rect& viewport;
@@ -136,6 +134,9 @@
 
         // Configure layer settings for using blurs
         BlurSetting blurSetting;
+
+        // Requested white point of the layer in nits
+        const float whitePointNits;
     };
 
     // A superset of LayerSettings required by RenderEngine to compose a layer
@@ -155,7 +156,7 @@
             ClientCompositionTargetSettings&) = 0;
 
     // Called after the layer is displayed to update the presentation fence
-    virtual void onLayerDisplayed(const sp<Fence>&) = 0;
+    virtual void onLayerDisplayed(std::shared_future<renderengine::RenderEngineResult>) = 0;
 
     // Gets some kind of identifier for the layer for debug purposes.
     virtual const char* getDebugName() const = 0;
@@ -165,6 +166,7 @@
 
     // Whether the layer should be rendered with rounded corners.
     virtual bool hasRoundedCorners() const = 0;
+    virtual void setWasClientComposed(const sp<Fence>&) {}
 };
 
 // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
@@ -177,11 +179,10 @@
 
 static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
                               const LayerFE::ClientCompositionTargetSettings& rhs) {
-    return lhs.clip.hasSameRects(rhs.clip) &&
-            lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
+    return lhs.clip.hasSameRects(rhs.clip) && lhs.needsFiltering == rhs.needsFiltering &&
+            lhs.isSecure == rhs.isSecure &&
             lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
-            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
-            lhs.dataspace == rhs.dataspace &&
+            lhs.viewport == rhs.viewport && lhs.dataspace == rhs.dataspace &&
             lhs.realContentIsVisible == rhs.realContentIsVisible &&
             lhs.clearContent == rhs.clearContent;
 }
@@ -202,8 +203,6 @@
     *os << "\n    .needsFiltering = " << settings.needsFiltering;
     *os << "\n    .isSecure = " << settings.isSecure;
     *os << "\n    .supportsProtectedContent = " << settings.supportsProtectedContent;
-    *os << "\n    .clearRegion = ";
-    PrintTo(settings.clearRegion, os);
     *os << "\n    .viewport = ";
     PrintTo(settings.viewport, os);
     *os << "\n    .dataspace = ";
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index a45be8a..8bf7f8f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -22,6 +22,7 @@
 #include <math/mat4.h>
 #include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -38,6 +39,8 @@
 
 #include "DisplayHardware/Hal.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
@@ -93,11 +96,9 @@
     /*
      * Visibility state
      */
-    // the layer stack this layer belongs to
-    std::optional<uint32_t> layerStackId;
 
-    // If true, this layer should be only visible on the internal display
-    bool internalOnly{false};
+    // The filter that determines which outputs include this layer
+    ui::LayerFilter outputFilter;
 
     // If false, this layer should not be considered visible
     bool isVisible{true};
@@ -157,16 +158,19 @@
      */
 
     // The type of composition for this layer
-    hal::Composition compositionType{hal::Composition::INVALID};
+    aidl::android::hardware::graphics::composer3::Composition compositionType{
+            aidl::android::hardware::graphics::composer3::Composition::INVALID};
 
     // The buffer and related state
     sp<GraphicBuffer> buffer;
     int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
-    sp<Fence> acquireFence;
+    sp<Fence> acquireFence = Fence::NO_FENCE;
     Region surfaceDamage;
 
     // The handle to use for a sideband stream for this layer
     sp<NativeHandle> sidebandStream;
+    // If true, this sideband layer has a frame update
+    bool sidebandStreamHasFrame{false};
 
     // The color for this layer
     half4 color;
@@ -199,6 +203,9 @@
     // The output-independent frame for the cursor
     Rect cursorFrame;
 
+    // framerate of the layer as measured by LayerHistory
+    float fps;
+
     virtual ~LayerFECompositionState();
 
     // Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 0ef0b99..6cb12dd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -28,6 +28,7 @@
 #include <renderengine/LayerSettings.h>
 #include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
+#include <ui/LayerStack.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
 #include <utils/StrongPointer.h>
@@ -172,6 +173,8 @@
     // Sets the projection state to use
     virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                                const Rect& orientedDisplaySpaceRect) = 0;
+    // Sets the brightness that will take effect next frame.
+    virtual void setNextBrightness(float brightness) = 0;
     // Sets the bounds to use
     virtual void setDisplaySize(const ui::Size&) = 0;
     // Gets the transform hint used in layers that belong to this output. Used to guide
@@ -180,9 +183,8 @@
     // output.
     virtual ui::Transform::RotationFlags getTransformHint() const = 0;
 
-    // Sets the layer stack filtering settings for this output. See
-    // belongsInOutput for full details.
-    virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
+    // Sets the filter for this output. See Output::includesLayer.
+    virtual void setLayerFilter(ui::LayerFilter) = 0;
 
     // Sets the output color mode
     virtual void setColorProfile(const ColorProfile&) = 0;
@@ -221,20 +223,12 @@
     virtual OutputCompositionState& editState() = 0;
 
     // Gets the dirty region in layer stack space.
-    // If repaintEverything is true, this will be the full display bounds.
-    virtual Region getDirtyRegion(bool repaintEverything) const = 0;
+    virtual Region getDirtyRegion() const = 0;
 
-    // Tests whether a given layerStackId belongs in this output.
-    // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
-    // unless the layer should display on the primary output only and this is not the primary output
-
-    // A layer belongs to the output if its layerStackId matches. Additionally
-    // if the layer should only show in the internal (primary) display only and
-    // this output allows that.
-    virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
-
-    // Determines if a layer belongs to the output.
-    virtual bool belongsInOutput(const sp<LayerFE>&) const = 0;
+    // Returns whether the output includes a layer, based on their respective filters.
+    // See Output::setLayerFilter.
+    virtual bool includesLayer(ui::LayerFilter) const = 0;
+    virtual bool includesLayer(const sp<LayerFE>&) const = 0;
 
     // Returns a pointer to the output layer corresponding to the given layer on
     // this output, or nullptr if the layer does not have one
@@ -294,7 +288,8 @@
     virtual bool getSkipColorTransform() const = 0;
     virtual FrameFences presentAndGetFrameFences() = 0;
     virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
-            bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
+            bool supportsProtectedContent, ui::Dataspace outputDataspace,
+            std::vector<LayerFE*> &outLayerRef) = 0;
     virtual void appendRegionFlashRequests(
             const Region& flashRegion,
             std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index ead941d..a2824f5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -33,6 +33,8 @@
 
 #include "LayerFE.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
@@ -112,7 +114,8 @@
     virtual bool isHardwareCursor() const = 0;
 
     // Applies a HWC device requested composition type change
-    virtual void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) = 0;
+    virtual void applyDeviceCompositionTypeChange(
+            aidl::android::hardware::graphics::composer3::Composition) = 0;
 
     // Prepares to apply any HWC device layer requests
     virtual void prepareForDeviceLayerRequests() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 58bb41a..a63145a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -28,51 +28,36 @@
 
 // Geometrical space to which content is projected.
 // For example, this can be the layer space or the physical display space.
-struct ProjectionSpace {
+class ProjectionSpace {
+public:
     ProjectionSpace() = default;
-    ProjectionSpace(ui::Size size, Rect content)
-          : bounds(std::move(size)), content(std::move(content)) {}
-
-    // Bounds of this space. Always starts at (0,0).
-    Rect bounds;
-
-    // Rect onto which content is projected.
-    Rect content;
-
-    // The orientation of this space. This value is meaningful only in relation to the rotation
-    // of another projection space and it's used to determine the rotating transformation when
-    // mapping between the two.
-    // As a convention when using this struct orientation = 0 for the "oriented*" projection
-    // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
-    // of the display space will become 90, while  the orientation of the layer stack space will
-    // remain the same.
-    ui::Rotation orientation = ui::ROTATION_0;
+    ProjectionSpace(ui::Size size, Rect content) : mBounds(size), mContent(std::move(content)) {}
 
     // Returns a transform which maps this.content into destination.content
     // and also rotates according to this.orientation and destination.orientation
     ui::Transform getTransform(const ProjectionSpace& destination) const {
-        ui::Rotation rotation = destination.orientation - orientation;
+        ui::Rotation rotation = destination.getOrientation() - mOrientation;
 
         // Compute a transformation which rotates the destination in a way it has the same
         // orientation as us.
         const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
         ui::Transform inverseRotatingTransform;
-        inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
-                                     destination.bounds.height());
+        inverseRotatingTransform.set(inverseRotationFlags, destination.getBounds().width,
+                                     destination.getBounds().height);
         // The destination content rotated so it has the same orientation as us.
-        Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+        Rect orientedDestContent = inverseRotatingTransform.transform(destination.getContent());
 
         // Compute translation from the source content to (0, 0).
-        const float sourceX = content.left;
-        const float sourceY = content.top;
+        const float sourceX = mContent.left;
+        const float sourceY = mContent.top;
         ui::Transform sourceTranslation;
         sourceTranslation.set(-sourceX, -sourceY);
 
         // Compute scaling transform which maps source content to destination content, assuming
         // they are both at (0, 0).
         ui::Transform scale;
-        const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
-        const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+        const float scaleX = static_cast<float>(orientedDestContent.width()) / mContent.width();
+        const float scaleY = static_cast<float>(orientedDestContent.height()) / mContent.height();
         scale.set(scaleX, 0, 0, scaleY);
 
         // Compute translation from (0, 0) to the orientated destination content.
@@ -83,8 +68,8 @@
 
         // Compute rotation transform.
         const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
-        auto orientedDestWidth = destination.bounds.width();
-        auto orientedDestHeight = destination.bounds.height();
+        auto orientedDestWidth = destination.getBounds().width;
+        auto orientedDestHeight = destination.getBounds().height;
         if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
             std::swap(orientedDestWidth, orientedDestHeight);
         }
@@ -98,9 +83,39 @@
     }
 
     bool operator==(const ProjectionSpace& other) const {
-        return bounds == other.bounds && content == other.content &&
-                orientation == other.orientation;
+        return mBounds == other.mBounds && mContent == other.mContent &&
+                mOrientation == other.mOrientation;
     }
+
+    void setBounds(ui::Size newBounds) { mBounds = std::move(newBounds); }
+
+    void setContent(Rect newContent) { mContent = std::move(newContent); }
+
+    void setOrientation(ui::Rotation newOrientation) { mOrientation = newOrientation; }
+
+    Rect getBoundsAsRect() const { return Rect(mBounds.getWidth(), mBounds.getHeight()); }
+
+    const ui::Size& getBounds() const { return mBounds; }
+
+    const Rect& getContent() const { return mContent; }
+
+    ui::Rotation getOrientation() const { return mOrientation; }
+
+private:
+    // Bounds of this space. Always starts at (0,0).
+    ui::Size mBounds = ui::Size();
+
+    // Rect onto which content is projected.
+    Rect mContent = Rect();
+
+    // The orientation of this space. This value is meaningful only in relation to the rotation
+    // of another projection space and it's used to determine the rotating transformation when
+    // mapping between the two.
+    // As a convention when using this struct orientation = 0 for the "oriented*" projection
+    // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+    // of the display space will become 90, while  the orientation of the layer stack space will
+    // remain the same.
+    ui::Rotation mOrientation = ui::ROTATION_0;
 };
 
 } // namespace compositionengine
@@ -108,8 +123,8 @@
 inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
     return android::base::
             StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
-                         to_string(space.bounds).c_str(), to_string(space.content).c_str(),
-                         toCString(space.orientation));
+                         to_string(space.getBoundsAsRect()).c_str(),
+                         to_string(space.getContent()).c_str(), toCString(space.getOrientation()));
 }
 
 // Defining PrintTo helps with Google Tests.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index bb540ea..3571e11 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -76,16 +76,15 @@
     virtual void applyChangedTypesToLayers(const ChangedTypes&);
     virtual void applyDisplayRequests(const DisplayRequests&);
     virtual void applyLayerRequestsToLayers(const LayerRequests&);
-    virtual void applyClientTargetRequests(const ClientTargetProperty&);
+    virtual void applyClientTargetRequests(const ClientTargetProperty&, float whitePointNits);
 
     // Internal
     virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
 
 private:
-    bool mIsVirtual = false;
-    bool mIsDisconnected = false;
     DisplayId mId;
+    bool mIsDisconnected = false;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
index 6b9597b..7521324 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -22,6 +22,7 @@
 
 #include <math/mat4.h>
 #include <ui/FloatRect.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/StretchEffect.h>
@@ -52,13 +53,14 @@
     dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
 }
 
-void dumpVal(std::string& out, const char* name, const FloatRect& rect);
-void dumpVal(std::string& out, const char* name, const Rect& rect);
-void dumpVal(std::string& out, const char* name, const Region& region);
-void dumpVal(std::string& out, const char* name, const ui::Transform&);
-void dumpVal(std::string& out, const char* name, const ui::Size&);
+void dumpVal(std::string& out, const char* name, ui::LayerFilter);
+void dumpVal(std::string& out, const char* name, ui::Size);
 
-void dumpVal(std::string& out, const char* name, const mat4& tr);
+void dumpVal(std::string& out, const char* name, const FloatRect&);
+void dumpVal(std::string& out, const char* name, const Rect&);
+void dumpVal(std::string& out, const char* name, const Region&);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+void dumpVal(std::string& out, const char* name, const mat4&);
 void dumpVal(std::string& out, const char* name, const StretchEffect&);
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index ddcc907..a7a8e97 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -23,6 +23,7 @@
 #include <compositionengine/impl/planner/Planner.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/LayerSettings.h>
+
 #include <memory>
 #include <utility>
 #include <vector>
@@ -44,8 +45,9 @@
     void setLayerCachingTexturePoolEnabled(bool) override;
     void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                        const Rect& orientedDisplaySpaceRect) override;
+    void setNextBrightness(float brightness) override;
     void setDisplaySize(const ui::Size&) override;
-    void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+    void setLayerFilter(ui::LayerFilter) override;
     ui::Transform::RotationFlags getTransformHint() const override;
 
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
@@ -64,9 +66,10 @@
     compositionengine::RenderSurface* getRenderSurface() const override;
     void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
 
-    Region getDirtyRegion(bool repaintEverything) const override;
-    bool belongsInOutput(std::optional<uint32_t>, bool) const override;
-    bool belongsInOutput(const sp<LayerFE>&) const override;
+    Region getDirtyRegion() const override;
+
+    bool includesLayer(ui::LayerFilter) const override;
+    bool includesLayer(const sp<LayerFE>&) const override;
 
     compositionengine::OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const override;
 
@@ -111,8 +114,8 @@
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
     std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
-            bool supportsProtectedContent, Region& clearRegion,
-            ui::Dataspace outputDataspace) override;
+          bool supportsProtectedContent, ui::Dataspace outputDataspace,
+          std::vector<LayerFE*> &outLayerFEs) override;
     void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
     void setExpensiveRenderingExpected(bool enabled) override;
     void dumpBase(std::string&) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f34cb94..cc7c257 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -32,6 +32,7 @@
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <compositionengine/ProjectionSpace.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -59,11 +60,8 @@
     // If true, the current frame reused the buffer from a previous client composition
     bool reusedClientComposition{false};
 
-    // If true, this output displays layers that are internal-only
-    bool layerStackInternal{false};
-
-    // The layer stack to display on this display
-    uint32_t layerStackId{~0u};
+    // The conditions for including a layer on this output
+    ui::LayerFilter layerFilter;
 
     // The common space for all layers in the layer stack. layerStackSpace.content is the Rect
     // which gets projected on the display. The orientation of this space is always ROTATION_0.
@@ -123,12 +121,22 @@
     // to prevent an early presentation of a frame.
     std::shared_ptr<FenceTime> previousPresentFence;
 
+    // The expected time for the next present
+    nsecs_t expectedPresentTime{0};
+
     // Current display brightness
     float displayBrightnessNits{-1.f};
 
     // SDR white point
     float sdrWhitePointNits{-1.f};
 
+    // White point of the client target
+    float clientTargetWhitePointNits{-1.f};
+
+    // Display brightness that will take effect this frame.
+    // This is slightly distinct from nits, in that nits cannot be passed to hw composer.
+    std::optional<float> displayBrightness = std::nullopt;
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 244f8ab..0082dac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -27,6 +27,8 @@
 
 #include "DisplayHardware/DisplayIdentification.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
 namespace android::compositionengine {
 
 struct LayerFECompositionState;
@@ -50,7 +52,8 @@
     HWC2::Layer* getHwcLayer() const override;
     bool requiresClientComposition() const override;
     bool isHardwareCursor() const override;
-    void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) override;
+    void applyDeviceCompositionTypeChange(
+            aidl::android::hardware::graphics::composer3::Composition) override;
     void prepareForDeviceLayerRequests() override;
     void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
     bool needsFiltering() const override;
@@ -68,20 +71,24 @@
 
 private:
     Rect calculateInitialCrop() const;
-    void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
-                                                uint32_t z);
+    void writeOutputDependentGeometryStateToHWC(
+            HWC2::Layer*, aidl::android::hardware::graphics::composer3::Composition, uint32_t z);
     void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
                                                   bool skipLayer);
     void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
-    void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
-                                                  bool skipLayer);
+    void writeOutputIndependentPerFrameStateToHWC(
+            HWC2::Layer*, const LayerFECompositionState&,
+            aidl::android::hardware::graphics::composer3::Composition compositionType,
+            bool skipLayer);
     void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&, bool skipLayer);
-    void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+    void writeCompositionTypeToHWC(HWC2::Layer*,
+                                   aidl::android::hardware::graphics::composer3::Composition,
                                    bool isPeekingThrough, bool skipLayer);
-    void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
-                                               Hwc2::IComposerClient::Composition to) const;
+    void detectDisallowedCompositionTypeChange(
+            aidl::android::hardware::graphics::composer3::Composition from,
+            aidl::android::hardware::graphics::composer3::Composition to) const;
     bool isClientCompositionForced(bool isPeekingThrough) const;
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 7564c54..49cb912 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -35,6 +35,8 @@
 
 #include "DisplayHardware/ComposerHal.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
@@ -123,8 +125,8 @@
         std::shared_ptr<HWC2::Layer> hwcLayer;
 
         // The most recently set HWC composition type for this layer
-        Hwc2::IComposerClient::Composition hwcCompositionType{
-                Hwc2::IComposerClient::Composition::INVALID};
+        aidl::android::hardware::graphics::composer3::Composition hwcCompositionType{
+                aidl::android::hardware::graphics::composer3::Composition::INVALID};
 
         // The buffer cache for this layer. This is used to lower the
         // cost of sending reused buffers to the HWC.
@@ -146,6 +148,10 @@
 
     // Timestamp for when the layer is queued for client composition
     nsecs_t clientCompositionTimestamp{0};
+
+    // White point of the layer, in nits.
+    static constexpr float kDefaultWhitePointNits = 200.f;
+    float whitePointNits = kDefaultWhitePointNits;
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index a040fa9..92cc484 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -87,6 +87,13 @@
         const bool mEnableHolePunch;
     };
 
+    // Constants not yet backed by a sysprop
+    // CachedSets that contain no more than this many layers may be considered inactive on the basis
+    // of FPS.
+    static constexpr int kNumLayersFpsConsideration = 1;
+    // Frames/Second threshold below which these CachedSets may be considered inactive.
+    static constexpr float kFpsActiveThreshold = 1.f;
+
     Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables);
 
     void setDisplaySize(ui::Size size) {
@@ -119,29 +126,30 @@
                              std::chrono::steady_clock::time_point now);
 
     // A Run is a sequence of CachedSets, which is a candidate for flattening into a single
-    // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1
-    // CachedSet
+    // CachedSet. Because it is wasteful to flatten 1 CachedSet, a run must contain more than
+    // 1 CachedSet or be used for a hole punch.
     class Run {
     public:
         // A builder for a Run, to aid in construction
         class Builder {
         private:
             std::vector<CachedSet>::const_iterator mStart;
-            std::vector<size_t> mLengths;
+            int32_t mNumSets = 0;
             const CachedSet* mHolePunchCandidate = nullptr;
             const CachedSet* mBlurringLayer = nullptr;
+            bool mBuilt = false;
 
         public:
             // Initializes a Builder a CachedSet to start from.
             // This start iterator must be an iterator for mLayers
             void init(const std::vector<CachedSet>::const_iterator& start) {
                 mStart = start;
-                mLengths.push_back(start->getLayerCount());
+                mNumSets = 1;
             }
 
             // Appends a new CachedSet to the end of the run
             // The provided length must be the size of the next sequential CachedSet in layers
-            void append(size_t length) { mLengths.push_back(length); }
+            void increment() { mNumSets++; }
 
             // Sets the hole punch candidate for the Run.
             void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
@@ -154,13 +162,36 @@
 
             // Builds a Run instance, if a valid Run may be built.
             std::optional<Run> validateAndBuild() {
-                if (mLengths.size() <= 1) {
+                const bool built = mBuilt;
+                mBuilt = true;
+                if (mNumSets <= 0 || built) {
                     return std::nullopt;
                 }
 
+                const bool requiresHolePunch =
+                        mHolePunchCandidate && mHolePunchCandidate->requiresHolePunch();
+
+                if (!requiresHolePunch) {
+                    // If we don't require a hole punch, then treat solid color layers at the front
+                    // to be "cheap", so remove them from the candidate cached set.
+                    while (mNumSets > 1 && mStart->getLayerCount() == 1 &&
+                           mStart->getFirstLayer().getBuffer() == nullptr) {
+                        mStart++;
+                        mNumSets--;
+                    }
+
+                    // Only allow for single cached sets if a hole punch is required. If we're here,
+                    // then we don't require a hole punch, so don't build a run.
+                    if (mNumSets <= 1) {
+                        return std::nullopt;
+                    }
+                }
+
                 return Run(mStart,
-                           std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
-                                       [](size_t left, size_t right) { return left + right; }),
+                           std::reduce(mStart, mStart + mNumSets, 0u,
+                                       [](size_t length, const CachedSet& set) {
+                                           return length + set.getLayerCount();
+                                       }),
                            mHolePunchCandidate, mBlurringLayer);
             }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 5237527..14324de 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -28,6 +28,8 @@
 #include "DisplayHardware/Hal.h"
 #include "math/HashCombine.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
 namespace std {
 template <typename T>
 struct hash<android::sp<T>> {
@@ -232,7 +234,7 @@
         return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
     }
     int32_t getBackgroundBlurRadius() const { return mBackgroundBlurRadius.get(); }
-    hardware::graphics::composer::hal::Composition getCompositionType() const {
+    aidl::android::hardware::graphics::composer3::Composition getCompositionType() const {
         return mCompositionType.get();
     }
 
@@ -245,6 +247,7 @@
     bool isProtected() const {
         return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
     }
+    float getFps() const { return getOutputLayer()->getLayerFE().getCompositionState()->fps; }
 
     void dump(std::string& result) const;
     std::optional<std::string> compare(const LayerState& other) const;
@@ -369,17 +372,18 @@
 
     OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
 
-    using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
-                                                  LayerStateField::CompositionType>;
-    CompositionTypeState
-            mCompositionType{[](auto layer) {
-                                 return layer->getState().forceClientComposition
-                                         ? hardware::graphics::composer::hal::Composition::CLIENT
-                                         : layer->getLayerFE()
-                                                   .getCompositionState()
-                                                   ->compositionType;
-                             },
-                             CompositionTypeState::getHalToStrings()};
+    using CompositionTypeState =
+            OutputLayerState<aidl::android::hardware::graphics::composer3::Composition,
+                             LayerStateField::CompositionType>;
+    CompositionTypeState mCompositionType{[](auto layer) {
+                                              return layer->getState().forceClientComposition
+                                                      ? aidl::android::hardware::graphics::
+                                                                composer3::Composition::CLIENT
+                                                      : layer->getLayerFE()
+                                                                .getCompositionState()
+                                                                ->compositionType;
+                                          },
+                                          CompositionTypeState::getHalToStrings()};
 
     OutputLayerState<void*, LayerStateField::SidebandStream>
             mSidebandStream{[](auto layer) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index fe486d3..ef1560e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -109,7 +109,7 @@
     static std::optional<Plan> fromString(const std::string&);
 
     void reset() { mLayerTypes.clear(); }
-    void addLayerType(hardware::graphics::composer::hal::Composition type) {
+    void addLayerType(aidl::android::hardware::graphics::composer3::Composition type) {
         mLayerTypes.emplace_back(type);
     }
 
@@ -125,7 +125,7 @@
     }
 
 private:
-    std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
+    std::vector<aidl::android::hardware::graphics::composer3::Composition> mLayerTypes;
 };
 
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index d215bda..16aebef 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -39,7 +39,7 @@
                  std::vector<compositionengine::LayerFE::LayerSettings>(
                          compositionengine::LayerFE::ClientCompositionTargetSettings&));
 
-    MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
+    MOCK_METHOD1(onLayerDisplayed, void(std::shared_future<renderengine::RenderEngineResult>));
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 8fdf3ae..b68b95d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -39,10 +39,14 @@
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool));
     MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
+    MOCK_METHOD1(setNextBrightness, void(float));
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
-    MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
     MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags());
 
+    MOCK_METHOD(void, setLayerFilter, (ui::LayerFilter));
+    MOCK_METHOD(bool, includesLayer, (ui::LayerFilter), (const));
+    MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&), (const));
+
     MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
     MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
     MOCK_METHOD2(setDisplayBrightness, void(float, float));
@@ -62,9 +66,7 @@
     MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
     MOCK_METHOD0(editState, OutputCompositionState&());
 
-    MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
-    MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
-    MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+    MOCK_METHOD(Region, getDirtyRegion, (), (const));
 
     MOCK_CONST_METHOD1(getOutputLayerForLayer,
                        compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
@@ -114,7 +116,7 @@
     MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
 
     MOCK_METHOD3(generateClientCompositionRequests,
-                 std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+                 std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&));
     MOCK_METHOD2(appendRegionFlashRequests,
                  void(const Region&, std::vector<LayerFE::LayerSettings>&));
     MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 358ed5a..a6cb811 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -46,7 +46,8 @@
     MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
     MOCK_CONST_METHOD0(requiresClientComposition, bool());
     MOCK_CONST_METHOD0(isHardwareCursor, bool());
-    MOCK_METHOD1(applyDeviceCompositionTypeChange, void(Hwc2::IComposerClient::Composition));
+    MOCK_METHOD1(applyDeviceCompositionTypeChange,
+                 void(aidl::android::hardware::graphics::composer3::Composition));
     MOCK_METHOD0(prepareForDeviceLayerRequests, void());
     MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
     MOCK_CONST_METHOD0(needsFiltering, bool());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 2f2c686..186e191 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -39,6 +39,8 @@
 
 #include "DisplayHardware/PowerAdvisor.h"
 
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
+
 namespace android::compositionengine::impl {
 
 std::shared_ptr<Display> createDisplay(
@@ -51,12 +53,9 @@
 
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
     mId = args.id;
-    mIsVirtual = !args.connectionType;
     mPowerAdvisor = args.powerAdvisor;
     editState().isSecure = args.isSecure;
-    editState().displaySpace.bounds = Rect(args.pixels);
-    setLayerStackFilter(args.layerStackId,
-                        args.connectionType == ui::DisplayConnectionType::Internal);
+    editState().displaySpace.setBounds(args.pixels);
     setName(args.name);
 }
 
@@ -73,7 +72,7 @@
 }
 
 bool Display::isVirtual() const {
-    return mIsVirtual;
+    return VirtualDisplayId::tryCast(mId).has_value();
 }
 
 std::optional<DisplayId> Display::getDisplayId() const {
@@ -117,8 +116,8 @@
         return;
     }
 
-    if (mIsVirtual) {
-        ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+    if (isVirtual()) {
+        ALOGW("%s: Invalid operation on virtual display", __func__);
         return;
     }
 
@@ -136,7 +135,7 @@
     StringAppendF(&out, "   Composition Display State: [\"%s\"]", getName().c_str());
 
     out.append("\n   ");
-    dumpVal(out, "isVirtual", mIsVirtual);
+    dumpVal(out, "isVirtual", isVirtual());
     dumpVal(out, "DisplayId", to_string(mId));
     out.append("\n");
 
@@ -227,10 +226,22 @@
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
     auto& hwc = getCompositionEngine().getHwComposer();
+    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
+        physicalDisplayId && getState().displayBrightness) {
+        const status_t result =
+                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
+                                         Hwc2::Composer::DisplayBrightnessOptions{
+                                                 .applyImmediately = false})
+                        .get();
+        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
+                 getName().c_str(), result, strerror(-result));
+    }
+
     if (status_t result =
                 hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
                                                 getState().earliestPresentTime,
-                                                getState().previousPresentFence, &changes);
+                                                getState().previousPresentFence,
+                                                getState().expectedPresentTime, &changes);
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
               strerror(-result));
@@ -240,20 +251,23 @@
         applyChangedTypesToLayers(changes->changedTypes);
         applyDisplayRequests(changes->displayRequests);
         applyLayerRequestsToLayers(changes->layerRequests);
-        applyClientTargetRequests(changes->clientTargetProperty);
+        applyClientTargetRequests(changes->clientTargetProperty,
+                                  changes->clientTargetWhitePointNits);
     }
 
     // Determine what type of composition we are doing from the final state
     auto& state = editState();
     state.usesClientComposition = anyLayersRequireClientComposition();
     state.usesDeviceComposition = !allLayersRequireClientComposition();
+    // Clear out the display brightness now that it's been communicated to composer.
+    state.displayBrightness.reset();
 }
 
 bool Display::getSkipColorTransform() const {
     const auto& hwc = getCompositionEngine().getHwComposer();
     if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
         return hwc.hasDisplayCapability(*halDisplayId,
-                                        hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+                                        DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
     }
 
     return hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
@@ -284,7 +298,8 @@
 
         if (auto it = changedTypes.find(hwcLayer); it != changedTypes.end()) {
             layer->applyDeviceCompositionTypeChange(
-                    static_cast<Hwc2::IComposerClient::Composition>(it->second));
+                    static_cast<aidl::android::hardware::graphics::composer3::Composition>(
+                            it->second));
         }
     }
 }
@@ -312,12 +327,14 @@
     }
 }
 
-void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty,
+                                        float whitePointNits) {
     if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
         return;
     }
 
     editState().dataspace = clientTargetProperty.dataspace;
+    editState().clientTargetWhitePointNits = whitePointNits;
     getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
     getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
 }
@@ -364,8 +381,7 @@
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (GpuVirtualDisplayId::tryCast(mId) &&
-        getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+    if (GpuVirtualDisplayId::tryCast(mId) && getDirtyRegion().isEmpty()) {
         ALOGV("Skipping display composition");
         return;
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 5565396..01c368d 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -63,6 +63,18 @@
     dumpVal(out, name, valueName.c_str(), value);
 }
 
+void dumpVal(std::string& out, const char* name, ui::LayerFilter filter) {
+    out.append(name);
+    out.append("={");
+    dumpVal(out, "layerStack", filter.layerStack.id);
+    dumpVal(out, "toInternalDisplay", filter.toInternalDisplay);
+    out.push_back('}');
+}
+
+void dumpVal(std::string& out, const char* name, ui::Size size) {
+    StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
+}
+
 void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
     StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
 }
@@ -80,10 +92,6 @@
     out.append(" ");
 }
 
-void dumpVal(std::string& out, const char* name, const ui::Size& size) {
-    StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
-}
-
 void dumpVal(std::string& out, const char* name, const mat4& tr) {
     StringAppendF(&out,
                   "%s=["
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 95ae5e5..192ee04 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -27,6 +27,7 @@
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/impl/planner/Planner.h>
+#include <ftl/future.h>
 
 #include <thread>
 
@@ -156,65 +157,71 @@
                            const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
 
-    outputState.displaySpace.orientation = orientation;
-    LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+    outputState.displaySpace.setOrientation(orientation);
+    LOG_FATAL_IF(outputState.displaySpace.getBoundsAsRect() == Rect::INVALID_RECT,
                  "The display bounds are unknown.");
 
     // Compute orientedDisplaySpace
-    ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+    ui::Size orientedSize = outputState.displaySpace.getBounds();
     if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
         std::swap(orientedSize.width, orientedSize.height);
     }
-    outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
-    outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+    outputState.orientedDisplaySpace.setBounds(orientedSize);
+    outputState.orientedDisplaySpace.setContent(orientedDisplaySpaceRect);
 
     // Compute displaySpace.content
     const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
     ui::Transform rotation;
     if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
-        const auto displaySize = outputState.displaySpace.bounds;
+        const auto displaySize = outputState.displaySpace.getBoundsAsRect();
         rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
     }
-    outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+    outputState.displaySpace.setContent(rotation.transform(orientedDisplaySpaceRect));
 
     // Compute framebufferSpace
-    outputState.framebufferSpace.orientation = orientation;
-    LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+    outputState.framebufferSpace.setOrientation(orientation);
+    LOG_FATAL_IF(outputState.framebufferSpace.getBoundsAsRect() == Rect::INVALID_RECT,
                  "The framebuffer bounds are unknown.");
-    const auto scale =
-            getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
-    outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+    const auto scale = getScale(outputState.displaySpace.getBoundsAsRect(),
+                                outputState.framebufferSpace.getBoundsAsRect());
+    outputState.framebufferSpace.setContent(
+            outputState.displaySpace.getContent().scale(scale.x, scale.y));
 
     // Compute layerStackSpace
-    outputState.layerStackSpace.content = layerStackSpaceRect;
-    outputState.layerStackSpace.bounds = layerStackSpaceRect;
+    outputState.layerStackSpace.setContent(layerStackSpaceRect);
+    outputState.layerStackSpace.setBounds(
+            ui::Size(layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight()));
 
     outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
     outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
     dirtyEntireOutput();
 }
 
+void Output::setNextBrightness(float brightness) {
+    editState().displayBrightness = brightness;
+}
+
 void Output::setDisplaySize(const ui::Size& size) {
     mRenderSurface->setDisplaySize(size);
 
     auto& state = editState();
 
     // Update framebuffer space
-    const Rect newBounds(size);
-    state.framebufferSpace.bounds = newBounds;
+    const ui::Size newBounds(size);
+    state.framebufferSpace.setBounds(newBounds);
 
     // Update display space
-    state.displaySpace.bounds = newBounds;
+    state.displaySpace.setBounds(newBounds);
     state.transform = state.layerStackSpace.getTransform(state.displaySpace);
 
     // Update oriented display space
-    const auto orientation = state.displaySpace.orientation;
+    const auto orientation = state.displaySpace.getOrientation();
     ui::Size orientedSize = size;
     if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
         std::swap(orientedSize.width, orientedSize.height);
     }
-    const Rect newOrientedBounds(orientedSize);
-    state.orientedDisplaySpace.bounds = newOrientedBounds;
+    const ui::Size newOrientedBounds(orientedSize);
+    state.orientedDisplaySpace.setBounds(newOrientedBounds);
 
     if (mPlanner) {
         mPlanner->setDisplaySize(size);
@@ -227,11 +234,8 @@
     return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation());
 }
 
-void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
-    auto& outputState = editState();
-    outputState.layerStackId = layerStackId;
-    outputState.layerStackInternal = isInternal;
-
+void Output::setLayerFilter(ui::LayerFilter filter) {
+    editState().layerFilter = filter;
     dirtyEntireOutput();
 }
 
@@ -352,7 +356,7 @@
 void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
     mRenderSurface = std::move(surface);
     const auto size = mRenderSurface->getSize();
-    editState().framebufferSpace.bounds = Rect(size);
+    editState().framebufferSpace.setBounds(size);
     if (mPlanner) {
         mPlanner->setDisplaySize(size);
     }
@@ -371,26 +375,18 @@
     mRenderSurface = std::move(surface);
 }
 
-Region Output::getDirtyRegion(bool repaintEverything) const {
+Region Output::getDirtyRegion() const {
     const auto& outputState = getState();
-    Region dirty(outputState.layerStackSpace.content);
-    if (!repaintEverything) {
-        dirty.andSelf(outputState.dirtyRegion);
-    }
-    return dirty;
+    return outputState.dirtyRegion.intersect(outputState.layerStackSpace.getContent());
 }
 
-bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const {
-    // The layerStackId's must match, and also the layer must not be internal
-    // only when not on an internal output.
-    const auto& outputState = getState();
-    return layerStackId && (*layerStackId == outputState.layerStackId) &&
-            (!internalOnly || outputState.layerStackInternal);
+bool Output::includesLayer(ui::LayerFilter filter) const {
+    return getState().layerFilter.includes(filter);
 }
 
-bool Output::belongsInOutput(const sp<compositionengine::LayerFE>& layerFE) const {
+bool Output::includesLayer(const sp<LayerFE>& layerFE) const {
     const auto* layerFEState = layerFE->getCompositionState();
-    return layerFEState && belongsInOutput(layerFEState->layerStackId, layerFEState->internalOnly);
+    return layerFEState && includesLayer(layerFEState->outputFilter);
 }
 
 std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
@@ -461,7 +457,7 @@
 
     // Compute the resulting coverage for this output, and store it for later
     const ui::Transform& tr = outputState.transform;
-    Region undefinedRegion{outputState.displaySpace.bounds};
+    Region undefinedRegion{outputState.displaySpace.getBoundsAsRect()};
     undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
 
     outputState.undefinedRegion = undefinedRegion;
@@ -496,8 +492,8 @@
         layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
     }
 
-    // Only consider the layers on the given layer stack
-    if (!belongsInOutput(layerFE)) {
+    // Only consider the layers on this output
+    if (!includesLayer(layerFE)) {
         return;
     }
 
@@ -658,7 +654,7 @@
     // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
     const auto& outputState = getState();
     Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
-    drawRegion.andSelf(outputState.displaySpace.bounds);
+    drawRegion.andSelf(outputState.displaySpace.getBoundsAsRect());
     if (drawRegion.isEmpty()) {
         return;
     }
@@ -676,7 +672,7 @@
     outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
     outputLayerState.coveredRegion = coveredRegion;
     outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
-            visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
+            visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent()));
     outputLayerState.shadowRegion = shadowRegion;
 }
 
@@ -736,6 +732,7 @@
 
     editState().earliestPresentTime = refreshArgs.earliestPresentTime;
     editState().previousPresentFence = refreshArgs.previousPresentFence;
+    editState().expectedPresentTime = refreshArgs.expectedPresentTime;
 
     compositionengine::OutputLayer* peekThroughLayer = nullptr;
     sp<GraphicBuffer> previousOverride = nullptr;
@@ -911,7 +908,7 @@
 
 void Output::beginFrame() {
     auto& outputState = editState();
-    const bool dirty = !getDirtyRegion(false).isEmpty();
+    const bool dirty = !getDirtyRegion().isEmpty();
     const bool empty = getOutputLayerCount() == 0;
     const bool wasEmpty = !outputState.lastCompositionHadVisibleLayers;
 
@@ -963,14 +960,9 @@
     }
 
     if (getState().isEnabled) {
-        // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything);
-        if (!dirtyRegion.isEmpty()) {
-            base::unique_fd readyFence;
-            // redraw the whole screen
+        if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) {
             static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
-
-            mRenderSurface->queueBuffer(std::move(readyFence));
+            mRenderSurface->queueBuffer(base::unique_fd());
         }
     }
 
@@ -1049,19 +1041,18 @@
         }
     }
 
-    base::unique_fd readyFence;
     if (!hasClientComposition) {
         setExpensiveRenderingExpected(false);
-        return readyFence;
+        return base::unique_fd();
     }
 
     ALOGV("hasClientComposition");
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
-    clientCompositionDisplay.clip = outputState.layerStackSpace.content;
+    clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
+    clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
     clientCompositionDisplay.orientation =
-            ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+            ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
             ? outputState.dataspace
             : ui::Dataspace::UNKNOWN;
@@ -1071,21 +1062,19 @@
     clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
             ? outputState.displayBrightnessNits
             : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
-    clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits;
+    clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits;
 
     // Compute the global color transform matrix.
     if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
         clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
     }
 
-    // Note: Updated by generateClientCompositionRequests
-    clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
-
     // Generate the client composition requests for the layers on this output.
+    std::vector<LayerFE*> clientCompositionLayersFE;
     std::vector<LayerFE::LayerSettings> clientCompositionLayers =
             generateClientCompositionRequests(supportsProtectedContent,
-                                              clientCompositionDisplay.clearRegion,
-                                              clientCompositionDisplay.outputDataspace);
+                                              clientCompositionDisplay.outputDataspace,
+                                              clientCompositionLayersFE);
     appendRegionFlashRequests(debugRegion, clientCompositionLayers);
 
     // Check if the client composition requests were rendered into the provided graphic buffer. If
@@ -1096,7 +1085,7 @@
                                                    clientCompositionLayers)) {
             outputCompositionState.reusedClientComposition = true;
             setExpensiveRenderingExpected(false);
-            return readyFence;
+            return base::unique_fd();
         }
         mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
                                             clientCompositionLayers);
@@ -1114,12 +1103,12 @@
         setExpensiveRenderingExpected(true);
     }
 
-    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers;
-    clientCompositionLayerPointers.reserve(clientCompositionLayers.size());
+    std::vector<renderengine::LayerSettings> clientRenderEngineLayers;
+    clientRenderEngineLayers.reserve(clientCompositionLayers.size());
     std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
-                   std::back_inserter(clientCompositionLayerPointers),
-                   [](LayerFE::LayerSettings& settings) -> renderengine::LayerSettings* {
-                       return &settings;
+                   std::back_inserter(clientRenderEngineLayers),
+                   [](LayerFE::LayerSettings& settings) -> renderengine::LayerSettings {
+                       return settings;
                    });
 
     const nsecs_t renderEngineStart = systemTime();
@@ -1129,10 +1118,12 @@
     // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is
     // probably to encapsulate the output buffer into a structure that dispatches resource cleanup
     // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
-    const bool useFramebufferCache = outputState.layerStackInternal;
-    status_t status =
-            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
-                                    useFramebufferCache, std::move(fd), &readyFence);
+    const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay;
+    auto [status, drawFence] =
+            renderEngine
+                    .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, tex,
+                                useFramebufferCache, std::move(fd))
+                    .get();
 
     if (status != NO_ERROR && mClientCompositionRequestCache) {
         // If rendering was not successful, remove the request from the cache.
@@ -1140,27 +1131,32 @@
     }
 
     auto& timeStats = getCompositionEngine().getTimeStats();
-    if (readyFence.get() < 0) {
+    if (drawFence.get() < 0) {
         timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
     } else {
         timeStats.recordRenderEngineDuration(renderEngineStart,
                                              std::make_shared<FenceTime>(
-                                                     new Fence(dup(readyFence.get()))));
+                                                     new Fence(dup(drawFence.get()))));
     }
 
-    return readyFence;
+    if (clientCompositionLayersFE.size() > 0) {
+        sp<Fence> clientCompFence = new Fence(dup(drawFence.get()));
+        for (auto clientComposedLayer : clientCompositionLayersFE) {
+            clientComposedLayer->setWasClientComposed(clientCompFence);
+        }
+    }
+
+    return std::move(drawFence);
 }
 
 std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
-        bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
+      bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*>& outLayerFEs) {
     std::vector<LayerFE::LayerSettings> clientCompositionLayers;
     ALOGV("Rendering client layers");
 
     const auto& outputState = getState();
-    const Region viewportRegion(outputState.layerStackSpace.content);
+    const Region viewportRegion(outputState.layerStackSpace.getContent());
     bool firstLayer = true;
-    // Used when a layer clears part of the buffer.
-    Region stubRegion;
 
     bool disableBlurs = false;
     sp<GraphicBuffer> previousOverrideBuffer = nullptr;
@@ -1222,18 +1218,19 @@
                                                outputState.needsFiltering,
                                        .isSecure = outputState.isSecure,
                                        .supportsProtectedContent = supportsProtectedContent,
-                                       .clearRegion = clientComposition ? clearRegion : stubRegion,
-                                       .viewport = outputState.layerStackSpace.content,
+                                       .viewport = outputState.layerStackSpace.getContent(),
                                        .dataspace = outputDataspace,
                                        .realContentIsVisible = realContentIsVisible,
                                        .clearContent = !clientComposition,
-                                       .blurSetting = blurSetting};
+                                       .blurSetting = blurSetting,
+                                       .whitePointNits = layerState.whitePointNits};
                 results = layerFE.prepareClientCompositionList(targetSettings);
                 if (realContentIsVisible && !results.empty()) {
                     layer->editState().clientCompositionTimestamp = systemTime();
                 }
             }
 
+            outLayerFEs.push_back(&layerFE);
             clientCompositionLayers.insert(clientCompositionLayers.end(),
                                            std::make_move_iterator(results.begin()),
                                            std::make_move_iterator(results.end()));
@@ -1305,8 +1302,10 @@
             releaseFence =
                     Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
         }
-
-        layer->getLayerFE().onLayerDisplayed(releaseFence);
+        layer->getLayerFE().onLayerDisplayed(
+                ftl::yield<renderengine::RenderEngineResult>(
+                        {NO_ERROR, base::unique_fd(releaseFence->dup())})
+                        .share());
     }
 
     // We've got a list of layers needing fences, that are disjoint with
@@ -1314,7 +1313,9 @@
     // supply them with the present fence.
     for (auto& weakLayer : mReleasedLayers) {
         if (auto layer = weakLayer.promote(); layer != nullptr) {
-            layer->onLayerDisplayed(frame.presentFence);
+            layer->onLayerDisplayed(ftl::yield<renderengine::RenderEngineResult>(
+                                            {NO_ERROR, base::unique_fd(frame.presentFence->dup())})
+                                            .share());
         }
     }
 
@@ -1324,13 +1325,13 @@
 
 void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getState(), refreshArgs.nextInvalidateTime);
+        mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime);
     }
 }
 
 void Output::dirtyEntireOutput() {
     auto& outputState = editState();
-    outputState.dirtyRegion.set(outputState.displaySpace.bounds);
+    outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
 }
 
 void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index ee30ad8..acc9216 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -28,9 +28,7 @@
     dumpVal(out, "usesDeviceComposition", usesDeviceComposition);
     dumpVal(out, "flipClientTarget", flipClientTarget);
     dumpVal(out, "reusedClientComposition", reusedClientComposition);
-
-    dumpVal(out, "layerStack", layerStackId);
-    dumpVal(out, "layerStackInternal", layerStackInternal);
+    dumpVal(out, "layerFilter", layerFilter);
 
     out.append("\n   ");
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 56e9d27..95cc5a8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -24,6 +24,9 @@
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <cstdint>
+#include "system/graphics-base-v1.0.h"
+
+#include <ui/DataspaceUtils.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -34,6 +37,8 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
 
+using aidl::android::hardware::graphics::composer3::Composition;
+
 namespace android::compositionengine {
 
 OutputLayer::~OutputLayer() = default;
@@ -79,7 +84,7 @@
     FloatRect activeCropFloat =
             reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
 
-    const Rect& viewport = getOutput().getState().layerStackSpace.content;
+    const Rect& viewport = getOutput().getState().layerStackSpace.getContent();
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     // Transform to screen space.
@@ -136,7 +141,7 @@
          * buffer
          */
         uint32_t invTransformOrient =
-                ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+                ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
         // calculate the inverse transform
         if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
             invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -192,7 +197,7 @@
     Rect activeCrop = layerState.geomCrop;
     if (!activeCrop.isEmpty() && bufferSize.isValid()) {
         activeCrop = layerTransform.transform(activeCrop);
-        if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
+        if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) {
             activeCrop.clear();
         }
         activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -228,7 +233,7 @@
         geomLayerBounds.bottom += outset;
     }
     Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
-    if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
+    if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) {
         frame.clear();
     }
     const ui::Transform displayTransform{outputState.transform};
@@ -317,6 +322,14 @@
             ? outputState.targetDataspace
             : layerFEState->dataspace;
 
+    // For hdr content, treat the white point as the display brightness - HDR content should not be
+    // boosted or dimmed.
+    if (isHdrDataspace(state.dataspace)) {
+        state.whitePointNits = getOutput().getState().displayBrightnessNits;
+    } else {
+        state.whitePointNits = getOutput().getState().sdrWhitePointNits;
+    }
+
     // These are evaluated every frame as they can potentially change at any
     // time.
     if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
@@ -347,6 +360,10 @@
 
     auto requestedCompositionType = outputIndependentState->compositionType;
 
+    if (requestedCompositionType == Composition::SOLID_COLOR && state.overrideInfo.buffer) {
+        requestedCompositionType = Composition::DEVICE;
+    }
+
     // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
     // only when the geometry actually changes
     const bool isOverridden =
@@ -359,20 +376,22 @@
     }
 
     writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
-    writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, skipLayer);
+    writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState,
+                                             requestedCompositionType, skipLayer);
 
     writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
                               skipLayer);
 
-    // Always set the layer color after setting the composition type.
-    writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+    if (requestedCompositionType == Composition::SOLID_COLOR) {
+        writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+    }
 
     editState().hwc->stateOverridden = isOverridden;
     editState().hwc->layerSkipped = skipLayer;
 }
 
 void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
-                                                         hal::Composition requestedCompositionType,
+                                                         Composition requestedCompositionType,
                                                          uint32_t z) {
     const auto& outputDependentState = getState();
 
@@ -381,12 +400,7 @@
 
     if (outputDependentState.overrideInfo.buffer != nullptr) {
         displayFrame = outputDependentState.overrideInfo.displayFrame;
-        sourceCrop =
-                FloatRect(0.f, 0.f,
-                          static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
-                                                     ->getWidth()),
-                          static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
-                                                     ->getHeight()));
+        sourceCrop = displayFrame.toFloatRect();
     }
 
     ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
@@ -411,7 +425,7 @@
     }
 
     // Solid-color layers and overridden buffers should always use an identity transform.
-    const auto bufferTransform = (requestedCompositionType != hal::Composition::SOLID_COLOR &&
+    const auto bufferTransform = (requestedCompositionType != Composition::SOLID_COLOR &&
                                   getState().overrideInfo.buffer == nullptr)
             ? outputDependentState.bufferTransform
             : static_cast<hal::Transform>(0);
@@ -467,7 +481,7 @@
     if (auto error = hwcLayer->setVisibleRegion(visibleRegion); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
               to_string(error).c_str(), static_cast<int32_t>(error));
-        outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
+        visibleRegion.dump(LOG_TAG);
     }
 
     const auto dataspace = outputDependentState.overrideInfo.buffer
@@ -478,11 +492,21 @@
         ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
+
+    // Don't dim cached layers
+    const auto whitePointNits = outputDependentState.overrideInfo.buffer
+            ? getOutput().getState().displayBrightnessNits
+            : outputDependentState.whitePointNits;
+
+    if (auto error = hwcLayer->setWhitePointNits(whitePointNits); error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set white point %f: %s (%d)", getLayerFE().getDebugName(),
+              whitePointNits, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
 }
 
 void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
         HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
-        bool skipLayer) {
+        Composition compositionType, bool skipLayer) {
     switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
         case hal::Error::NONE:
             break;
@@ -506,19 +530,20 @@
     }
 
     // Content-specific per-frame state
-    switch (outputIndependentState.compositionType) {
-        case hal::Composition::SOLID_COLOR:
+    switch (compositionType) {
+        case Composition::SOLID_COLOR:
             // For compatibility, should be written AFTER the composition type.
             break;
-        case hal::Composition::SIDEBAND:
+        case Composition::SIDEBAND:
             writeSidebandStateToHWC(hwcLayer, outputIndependentState);
             break;
-        case hal::Composition::CURSOR:
-        case hal::Composition::DEVICE:
+        case Composition::CURSOR:
+        case Composition::DEVICE:
+        case Composition::DISPLAY_DECORATION:
             writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
             break;
-        case hal::Composition::INVALID:
-        case hal::Composition::CLIENT:
+        case Composition::INVALID:
+        case Composition::CLIENT:
             // Ignored
             break;
     }
@@ -526,14 +551,10 @@
 
 void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
                                             const LayerFECompositionState& outputIndependentState) {
-    if (outputIndependentState.compositionType != hal::Composition::SOLID_COLOR) {
-        return;
-    }
-
-    hal::Color color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
-                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
-                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
-                        255};
+    aidl::android::hardware::graphics::composer3::Color color = {outputIndependentState.color.r,
+                                                                 outputIndependentState.color.g,
+                                                                 outputIndependentState.color.b,
+                                                                 1.0f};
 
     if (auto error = hwcLayer->setColor(color); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set color: %s (%d)", getLayerFE().getDebugName(),
@@ -588,13 +609,13 @@
 }
 
 void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
-                                            hal::Composition requestedCompositionType,
+                                            Composition requestedCompositionType,
                                             bool isPeekingThrough, bool skipLayer) {
     auto& outputDependentState = editState();
 
     if (isClientCompositionForced(isPeekingThrough)) {
         // If we are forcing client composition, we need to tell the HWC
-        requestedCompositionType = hal::Composition::CLIENT;
+        requestedCompositionType = Composition::CLIENT;
     }
 
     // Set the requested composition type with the HWC whenever it changes
@@ -607,7 +628,7 @@
         if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
             error != hal::Error::NONE) {
             ALOGE("[%s] Failed to set composition type %s: %s (%d)", getLayerFE().getDebugName(),
-                  toString(requestedCompositionType).c_str(), to_string(error).c_str(),
+                  to_string(requestedCompositionType).c_str(), to_string(error).c_str(),
                   static_cast<int32_t>(error));
         }
     }
@@ -628,7 +649,7 @@
     const auto& outputState = getOutput().getState();
 
     Rect frame = layerFEState->cursorFrame;
-    frame.intersect(outputState.layerStackSpace.content, &frame);
+    frame.intersect(outputState.layerStackSpace.getContent(), &frame);
     Rect position = outputState.transform.transform(frame);
 
     if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
@@ -646,38 +667,38 @@
 
 bool OutputLayer::requiresClientComposition() const {
     const auto& state = getState();
-    return !state.hwc || state.hwc->hwcCompositionType == hal::Composition::CLIENT;
+    return !state.hwc || state.hwc->hwcCompositionType == Composition::CLIENT;
 }
 
 bool OutputLayer::isHardwareCursor() const {
     const auto& state = getState();
-    return state.hwc && state.hwc->hwcCompositionType == hal::Composition::CURSOR;
+    return state.hwc && state.hwc->hwcCompositionType == Composition::CURSOR;
 }
 
-void OutputLayer::detectDisallowedCompositionTypeChange(hal::Composition from,
-                                                        hal::Composition to) const {
+void OutputLayer::detectDisallowedCompositionTypeChange(Composition from, Composition to) const {
     bool result = false;
     switch (from) {
-        case hal::Composition::INVALID:
-        case hal::Composition::CLIENT:
+        case Composition::INVALID:
+        case Composition::CLIENT:
             result = false;
             break;
 
-        case hal::Composition::DEVICE:
-        case hal::Composition::SOLID_COLOR:
-            result = (to == hal::Composition::CLIENT);
+        case Composition::DEVICE:
+        case Composition::SOLID_COLOR:
+            result = (to == Composition::CLIENT);
             break;
 
-        case hal::Composition::CURSOR:
-        case hal::Composition::SIDEBAND:
-            result = (to == hal::Composition::CLIENT || to == hal::Composition::DEVICE);
+        case Composition::CURSOR:
+        case Composition::SIDEBAND:
+        case Composition::DISPLAY_DECORATION:
+            result = (to == Composition::CLIENT || to == Composition::DEVICE);
             break;
     }
 
     if (!result) {
         ALOGE("[%s] Invalid device requested composition type change: %s (%d) --> %s (%d)",
-              getLayerFE().getDebugName(), toString(from).c_str(), static_cast<int>(from),
-              toString(to).c_str(), static_cast<int>(to));
+              getLayerFE().getDebugName(), to_string(from).c_str(), static_cast<int>(from),
+              to_string(to).c_str(), static_cast<int>(to));
     }
 }
 
@@ -686,7 +707,7 @@
             (!isPeekingThrough && getLayerFE().hasRoundedCorners());
 }
 
-void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
+void OutputLayer::applyDeviceCompositionTypeChange(Composition compositionType) {
     auto& state = editState();
     LOG_FATAL_IF(!state.hwc);
     auto& hwcState = *state.hwc;
@@ -737,7 +758,7 @@
     // framebuffer space of the override buffer to layer space.
     const ProjectionSpace& layerSpace = getOutput().getState().layerStackSpace;
     const ui::Transform transform = getState().overrideInfo.displaySpace.getTransform(layerSpace);
-    const Rect boundaries = transform.transform(getState().overrideInfo.displayFrame);
+    const Rect boundaries = transform.transform(getState().overrideInfo.displaySpace.getContent());
 
     LayerFE::LayerSettings settings;
     settings.geometry = renderengine::Geometry{
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index ef50870..a19d23f 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -127,19 +127,18 @@
 }
 
 void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComposition) {
-    DisplaySurface::CompositionType compositionType;
-    if (usesClientComposition && usesDeviceComposition) {
-        compositionType = DisplaySurface::COMPOSITION_MIXED;
-    } else if (usesClientComposition) {
-        compositionType = DisplaySurface::COMPOSITION_GPU;
-    } else if (usesDeviceComposition) {
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    } else {
+    const auto compositionType = [=] {
+        using CompositionType = DisplaySurface::CompositionType;
+
+        if (usesClientComposition && usesDeviceComposition) return CompositionType::Mixed;
+        if (usesClientComposition) return CompositionType::Gpu;
+        if (usesDeviceComposition) return CompositionType::Hwc;
+
         // Nothing to do -- when turning the screen off we get a frame like
         // this. Call it a HWC frame since we won't be doing any GPU work but
         // will do a prepare/set cycle.
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    }
+        return CompositionType::Hwc;
+    }();
 
     if (status_t result = mDisplaySurface->prepareFrame(compositionType); result != NO_ERROR) {
         ALOGE("updateCompositionType failed for %s: %d (%s)", mDisplay.getName().c_str(), result,
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index c1cd5ab..2203f22 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -25,6 +25,7 @@
 #include <math/HashCombine.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/RenderEngine.h>
+#include <ui/DebugUtils.h>
 #include <utils/Trace.h>
 
 #include <utils/Trace.h>
@@ -159,30 +160,30 @@
 void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
                        const OutputCompositionState& outputState) {
     ATRACE_CALL();
-    const Rect& viewport = outputState.layerStackSpace.content;
+    const Rect& viewport = outputState.layerStackSpace.getContent();
     const ui::Dataspace& outputDataspace = outputState.dataspace;
     const ui::Transform::RotationFlags orientation =
-            ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+            ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation());
 
     renderengine::DisplaySettings displaySettings{
-            .physicalDisplay = outputState.framebufferSpace.content,
+            .physicalDisplay = outputState.framebufferSpace.getContent(),
             .clip = viewport,
             .outputDataspace = outputDataspace,
             .orientation = orientation,
+            .targetLuminanceNits = outputState.displayBrightnessNits,
     };
 
-    Region clearRegion = Region::INVALID_REGION;
     LayerFE::ClientCompositionTargetSettings targetSettings{
             .clip = Region(viewport),
             .needsFiltering = false,
             .isSecure = outputState.isSecure,
             .supportsProtectedContent = false,
-            .clearRegion = clearRegion,
             .viewport = viewport,
             .dataspace = outputDataspace,
             .realContentIsVisible = true,
             .clearContent = false,
             .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            .whitePointNits = outputState.displayBrightnessNits,
     };
 
     std::vector<renderengine::LayerSettings> layerSettings;
@@ -195,11 +196,6 @@
                              clientCompositionList.cend());
     }
 
-    std::vector<const renderengine::LayerSettings*> layerSettingsPointers;
-    std::transform(layerSettings.cbegin(), layerSettings.cend(),
-                   std::back_inserter(layerSettingsPointers),
-                   [](const renderengine::LayerSettings& settings) { return &settings; });
-
     renderengine::LayerSettings blurLayerSettings;
     if (mBlurLayer) {
         auto blurSettings = targetSettings;
@@ -214,7 +210,7 @@
         blurLayerSettings.name = std::string("blur layer");
         // Clear out the shadow settings
         blurLayerSettings.shadow = {};
-        layerSettingsPointers.push_back(&blurLayerSettings);
+        layerSettings.push_back(blurLayerSettings);
     }
 
     renderengine::LayerSettings holePunchSettings;
@@ -232,7 +228,7 @@
         holePunchSettings.disableBlending = true;
         holePunchSettings.alpha = 0.0f;
         holePunchSettings.name = std::string("hole punch layer");
-        layerSettingsPointers.push_back(&holePunchSettings);
+        layerSettings.push_back(holePunchSettings);
 
         // Add a solid background as the first layer in case there is no opaque
         // buffer behind the punch hole
@@ -241,7 +237,7 @@
         holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries;
         holePunchBackgroundSettings.geometry.positionTransform =
                 holePunchSettings.geometry.positionTransform;
-        layerSettingsPointers.insert(layerSettingsPointers.begin(), &holePunchBackgroundSettings);
+        layerSettings.emplace(layerSettings.begin(), holePunchBackgroundSettings);
     }
 
     if (sDebugHighlighLayers) {
@@ -259,7 +255,7 @@
                 .alpha = half(0.05f),
         };
 
-        layerSettingsPointers.emplace_back(&highlight);
+        layerSettings.emplace_back(highlight);
     }
 
     auto texture = texturePool.borrowTexture();
@@ -274,17 +270,17 @@
         bufferFence.reset(texture->getReadyFence()->dup());
     }
 
-    base::unique_fd drawFence;
-    status_t result =
-            renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false,
-                                    std::move(bufferFence), &drawFence);
+    auto [status, drawFence] = renderEngine
+                                       .drawLayers(displaySettings, layerSettings, texture->get(),
+                                                   false, std::move(bufferFence))
+                                       .get();
 
-    if (result == NO_ERROR) {
+    if (status == NO_ERROR) {
         mDrawFence = new Fence(drawFence.release());
         mOutputSpace = outputState.framebufferSpace;
         mTexture = texture;
         mTexture->setReadyFence(mDrawFence);
-        mOutputSpace.orientation = outputState.framebufferSpace.orientation;
+        mOutputSpace.setOrientation(outputState.framebufferSpace.getOrientation());
         mOutputDataspace = outputDataspace;
         mOrientation = orientation;
         mSkipCount = 0;
@@ -398,20 +394,29 @@
 
     if (mLayers.size() == 1) {
         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
-        base::StringAppendF(&result, "    Buffer %p", mLayers[0].getBuffer().get());
-        base::StringAppendF(&result, "    Protected [%s]",
+        if (auto* buffer = mLayers[0].getBuffer().get()) {
+            base::StringAppendF(&result, "    Buffer %p", buffer);
+            base::StringAppendF(&result, "    Format %s",
+                                decodePixelFormat(buffer->getPixelFormat()).c_str());
+        }
+        base::StringAppendF(&result, "    Protected [%s]\n",
                             mLayers[0].getState()->isProtected() ? "true" : "false");
     } else {
-        result.append("    Cached set of:");
+        result.append("    Cached set of:\n");
         for (const Layer& layer : mLayers) {
-            base::StringAppendF(&result, "\n      Layer [%s]", layer.getName().c_str());
-            base::StringAppendF(&result, "\n      Protected [%s]",
+            base::StringAppendF(&result, "      Layer [%s]\n", layer.getName().c_str());
+            if (auto* buffer = layer.getBuffer().get()) {
+                base::StringAppendF(&result, "       Buffer %p", buffer);
+                base::StringAppendF(&result, "    Format[%s]",
+                                    decodePixelFormat(buffer->getPixelFormat()).c_str());
+            }
+            base::StringAppendF(&result, "       Protected [%s]\n",
                                 layer.getState()->isProtected() ? "true" : "false");
         }
     }
 
-    base::StringAppendF(&result, "\n    Creation cost: %zd", getCreationCost());
-    base::StringAppendF(&result, "\n    Display cost: %zd\n", getDisplayCost());
+    base::StringAppendF(&result, "    Creation cost: %zd\n", getCreationCost());
+    base::StringAppendF(&result, "    Display cost: %zd\n", getDisplayCost());
 }
 
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index ad5e931..0918510 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -211,7 +211,8 @@
         displayCost += static_cast<size_t>(layer->getDisplayFrame().width() *
                                            layer->getDisplayFrame().height());
 
-        hasClientComposition |= layer->getCompositionType() == hal::Composition::CLIENT;
+        hasClientComposition |= layer->getCompositionType() ==
+                aidl::android::hardware::graphics::composer3::Composition::CLIENT;
     }
 
     if (hasClientComposition) {
@@ -315,7 +316,7 @@
                         state.overrideInfo = {
                                 .buffer = mNewCachedSet->getBuffer(),
                                 .acquireFence = mNewCachedSet->getDrawFence(),
-                                .displayFrame = mNewCachedSet->getTextureBounds(),
+                                .displayFrame = mNewCachedSet->getBounds(),
                                 .dataspace = mNewCachedSet->getOutputDataspace(),
                                 .displaySpace = mNewCachedSet->getOutputSpace(),
                                 .damageRegion = Region::INVALID_REGION,
@@ -355,7 +356,7 @@
                 state.overrideInfo = {
                         .buffer = currentLayerIter->getBuffer(),
                         .acquireFence = currentLayerIter->getDrawFence(),
-                        .displayFrame = currentLayerIter->getTextureBounds(),
+                        .displayFrame = currentLayerIter->getBounds(),
                         .dataspace = currentLayerIter->getOutputDataspace(),
                         .displaySpace = currentLayerIter->getOutputSpace(),
                         .damageRegion = Region(),
@@ -409,25 +410,29 @@
     bool runHasFirstLayer = false;
 
     for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
-        const bool layerIsInactive =
-                now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
+        bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
         const bool layerHasBlur = currentSet->hasBlurBehind();
 
+        // Layers should also be considered inactive whenever their framerate is lower than 1fps.
+        if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) {
+            auto layerFps = currentSet->getFirstLayer().getState()->getFps();
+            if (layerFps > 0 && layerFps <= kFpsActiveThreshold) {
+                ATRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f",
+                              currentSet->getFirstLayer().getName().c_str(), layerFps);
+                layerIsInactive = true;
+            }
+        }
+
         if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
             !currentSet->hasUnsupportedDataspace()) {
             if (isPartOfRun) {
-                builder.append(currentSet->getLayerCount());
+                builder.increment();
             } else {
-                // Runs can't start with a non-buffer layer
-                if (currentSet->getFirstLayer().getBuffer() == nullptr) {
-                    ALOGV("[%s] Skipping initial non-buffer layer", __func__);
-                } else {
-                    builder.init(currentSet);
-                    if (firstLayer) {
-                        runHasFirstLayer = true;
-                    }
-                    isPartOfRun = true;
+                builder.init(currentSet);
+                if (firstLayer) {
+                    runHasFirstLayer = true;
                 }
+                isPartOfRun = true;
             }
         } else if (isPartOfRun) {
             builder.setHolePunchCandidate(&(*currentSet));
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 936dba3..c79ca0d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -93,11 +93,7 @@
 
 void LayerState::dump(std::string& result) const {
     for (const StateInterface* field : getNonUniqueFields()) {
-        if (auto viewOpt = flag_name(field->getField()); viewOpt) {
-            base::StringAppendF(&result, "  %16s: ", std::string(*viewOpt).c_str());
-        } else {
-            result.append("<UNKNOWN FIELD>:\n");
-        }
+        base::StringAppendF(&result, "  %16s: ", ftl::flag_string(field->getField()).c_str());
 
         bool first = true;
         for (const std::string& line : field->toStrings()) {
@@ -126,11 +122,7 @@
             continue;
         }
 
-        if (auto viewOpt = flag_name(thisField->getField()); viewOpt) {
-            base::StringAppendF(&result, "  %16s: ", std::string(*viewOpt).c_str());
-        } else {
-            result.append("<UNKNOWN FIELD>:\n");
-        }
+        base::StringAppendF(&result, "  %16s: ", ftl::flag_string(thisField->getField()).c_str());
 
         const auto& thisStrings = thisField->toStrings();
         const auto& otherStrings = otherField->toStrings();
@@ -168,7 +160,8 @@
             lhs.mColorTransform == rhs.mColorTransform &&
             lhs.mCompositionType == rhs.mCompositionType &&
             lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
-            (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
+            (lhs.mCompositionType.get() !=
+                     aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR ||
              lhs.mSolidColor == rhs.mSolidColor);
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index f5b1cee..74d2701 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -193,7 +193,7 @@
 
         finalPlan.addLayerType(
                 forcedOrRequestedClient
-                        ? hardware::graphics::composer::hal::Composition::CLIENT
+                        ? aidl::android::hardware::graphics::composer3::Composition::CLIENT
                         : layer->getLayerFE().getCompositionState()->compositionType);
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 8226ef7..2d53583 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -39,8 +39,10 @@
 
         // Skip layers where both are client-composited, since that doesn't change the
         // composition plan
-        if (mLayers[i].getCompositionType() == hal::Composition::CLIENT &&
-            other[i]->getCompositionType() == hal::Composition::CLIENT) {
+        if (mLayers[i].getCompositionType() ==
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT &&
+            other[i]->getCompositionType() ==
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT) {
             continue;
         }
 
@@ -89,22 +91,32 @@
     for (char c : string) {
         switch (c) {
             case 'C':
-                plan.addLayerType(hal::Composition::CLIENT);
+                plan.addLayerType(
+                        aidl::android::hardware::graphics::composer3::Composition::CLIENT);
                 continue;
             case 'U':
-                plan.addLayerType(hal::Composition::CURSOR);
+                plan.addLayerType(
+                        aidl::android::hardware::graphics::composer3::Composition::CURSOR);
                 continue;
             case 'D':
-                plan.addLayerType(hal::Composition::DEVICE);
+                plan.addLayerType(
+                        aidl::android::hardware::graphics::composer3::Composition::DEVICE);
                 continue;
             case 'I':
-                plan.addLayerType(hal::Composition::INVALID);
+                plan.addLayerType(
+                        aidl::android::hardware::graphics::composer3::Composition::INVALID);
                 continue;
             case 'B':
-                plan.addLayerType(hal::Composition::SIDEBAND);
+                plan.addLayerType(
+                        aidl::android::hardware::graphics::composer3::Composition::SIDEBAND);
                 continue;
             case 'S':
-                plan.addLayerType(hal::Composition::SOLID_COLOR);
+                plan.addLayerType(
+                        aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR);
+                continue;
+            case 'A':
+                plan.addLayerType(aidl::android::hardware::graphics::composer3::Composition::
+                                          DISPLAY_DECORATION);
                 continue;
             default:
                 return std::nullopt;
@@ -117,24 +129,28 @@
     std::string result;
     for (auto type : plan.mLayerTypes) {
         switch (type) {
-            case hal::Composition::CLIENT:
+            case aidl::android::hardware::graphics::composer3::Composition::CLIENT:
                 result.append("C");
                 break;
-            case hal::Composition::CURSOR:
+            case aidl::android::hardware::graphics::composer3::Composition::CURSOR:
                 result.append("U");
                 break;
-            case hal::Composition::DEVICE:
+            case aidl::android::hardware::graphics::composer3::Composition::DEVICE:
                 result.append("D");
                 break;
-            case hal::Composition::INVALID:
+            case aidl::android::hardware::graphics::composer3::Composition::INVALID:
                 result.append("I");
                 break;
-            case hal::Composition::SIDEBAND:
+            case aidl::android::hardware::graphics::composer3::Composition::SIDEBAND:
                 result.append("B");
                 break;
-            case hal::Composition::SOLID_COLOR:
+            case aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR:
                 result.append("S");
                 break;
+            case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
+                // A for "Alpha", since the decoration is an alpha layer.
+                result.append("A");
+                break;
         }
     }
     return result;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index c037cc6..7dd4c21 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -37,6 +37,11 @@
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
 #include "MockPowerAdvisor.h"
+#include "ftl/future.h"
+
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
 
 namespace android::compositionengine {
 namespace {
@@ -44,6 +49,7 @@
 namespace hal = android::hardware::graphics::composer::hal;
 
 using testing::_;
+using testing::ByMove;
 using testing::DoAll;
 using testing::Eq;
 using testing::InSequence;
@@ -60,8 +66,7 @@
 constexpr HalVirtualDisplayId HAL_VIRTUAL_DISPLAY_ID{456u};
 constexpr GpuVirtualDisplayId GPU_VIRTUAL_DISPLAY_ID{789u};
 
-const ui::Size DEFAULT_RESOLUTION{1920, 1080};
-constexpr uint32_t DEFAULT_LAYER_STACK = 42;
+constexpr ui::Size DEFAULT_RESOLUTION{1920, 1080};
 
 struct Layer {
     Layer() {
@@ -161,13 +166,11 @@
         EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     }
 
-    DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
+    DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() {
         return DisplayCreationArgsBuilder()
                 .setId(DEFAULT_DISPLAY_ID)
-                .setConnectionType(ui::DisplayConnectionType::Internal)
                 .setPixels(DEFAULT_RESOLUTION)
                 .setIsSecure(true)
-                .setLayerStackId(DEFAULT_LAYER_STACK)
                 .setPowerAdvisor(&mPowerAdvisor)
                 .build();
     }
@@ -177,7 +180,6 @@
                 .setId(GPU_VIRTUAL_DISPLAY_ID)
                 .setPixels(DEFAULT_RESOLUTION)
                 .setIsSecure(false)
-                .setLayerStackId(DEFAULT_LAYER_STACK)
                 .setPowerAdvisor(&mPowerAdvisor)
                 .build();
     }
@@ -193,14 +195,13 @@
     using Display = DisplayTestCommon::PartialMockDisplay;
     std::shared_ptr<Display> mDisplay =
             createPartialMockDisplay<Display>(mCompositionEngine,
-                                              getDisplayCreationArgsForPhysicalHWCDisplay());
+                                              getDisplayCreationArgsForPhysicalDisplay());
 };
 
 struct FullDisplayImplTestCommon : public DisplayTestCommon {
     using Display = DisplayTestCommon::FullImplDisplay;
     std::shared_ptr<Display> mDisplay =
-            createDisplay<Display>(mCompositionEngine,
-                                   getDisplayCreationArgsForPhysicalHWCDisplay());
+            createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
 };
 
 struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon {
@@ -218,8 +219,7 @@
     LayerNoHWC2Layer mLayer3;
     StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
     std::shared_ptr<Display> mDisplay =
-            createDisplay<Display>(mCompositionEngine,
-                                   getDisplayCreationArgsForPhysicalHWCDisplay());
+            createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
 };
 
 /*
@@ -232,7 +232,7 @@
 
 TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) {
     auto display =
-            impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalHWCDisplay());
+            impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
     EXPECT_TRUE(display->isSecure());
     EXPECT_FALSE(display->isVirtual());
     EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId());
@@ -252,13 +252,11 @@
 
 using DisplaySetConfigurationTest = PartialMockDisplayTestCommon;
 
-TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) {
+TEST_F(DisplaySetConfigurationTest, configuresPhysicalDisplay) {
     mDisplay->setConfiguration(DisplayCreationArgsBuilder()
                                        .setId(DEFAULT_DISPLAY_ID)
-                                       .setConnectionType(ui::DisplayConnectionType::Internal)
                                        .setPixels(DEFAULT_RESOLUTION)
                                        .setIsSecure(true)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
                                        .setPowerAdvisor(&mPowerAdvisor)
                                        .setName(getDisplayNameFromCurrentTest())
                                        .build());
@@ -266,28 +264,11 @@
     EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
     EXPECT_TRUE(mDisplay->isSecure());
     EXPECT_FALSE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_TRUE(mDisplay->getState().layerStackInternal);
     EXPECT_FALSE(mDisplay->isValid());
-}
 
-TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) {
-    mDisplay->setConfiguration(DisplayCreationArgsBuilder()
-                                       .setId(DEFAULT_DISPLAY_ID)
-                                       .setConnectionType(ui::DisplayConnectionType::External)
-                                       .setPixels(DEFAULT_RESOLUTION)
-                                       .setIsSecure(false)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
-                                       .setPowerAdvisor(&mPowerAdvisor)
-                                       .setName(getDisplayNameFromCurrentTest())
-                                       .build());
-
-    EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
-    EXPECT_FALSE(mDisplay->isSecure());
-    EXPECT_FALSE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
-    EXPECT_FALSE(mDisplay->isValid());
+    const auto& filter = mDisplay->getState().layerFilter;
+    EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+    EXPECT_FALSE(filter.toInternalDisplay);
 }
 
 TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) {
@@ -295,7 +276,6 @@
                                        .setId(HAL_VIRTUAL_DISPLAY_ID)
                                        .setPixels(DEFAULT_RESOLUTION)
                                        .setIsSecure(false)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
                                        .setPowerAdvisor(&mPowerAdvisor)
                                        .setName(getDisplayNameFromCurrentTest())
                                        .build());
@@ -303,9 +283,11 @@
     EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
     EXPECT_FALSE(mDisplay->isValid());
+
+    const auto& filter = mDisplay->getState().layerFilter;
+    EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+    EXPECT_FALSE(filter.toInternalDisplay);
 }
 
 TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) {
@@ -313,7 +295,6 @@
                                        .setId(GPU_VIRTUAL_DISPLAY_ID)
                                        .setPixels(DEFAULT_RESOLUTION)
                                        .setIsSecure(false)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
                                        .setPowerAdvisor(&mPowerAdvisor)
                                        .setName(getDisplayNameFromCurrentTest())
                                        .build());
@@ -321,9 +302,11 @@
     EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
     EXPECT_FALSE(mDisplay->isValid());
+
+    const auto& filter = mDisplay->getState().layerFilter;
+    EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+    EXPECT_FALSE(filter.toInternalDisplay);
 }
 
 /*
@@ -579,7 +562,7 @@
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
     EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _))
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
             .WillOnce(Return(INVALID_OPERATION));
 
     mDisplay->chooseCompositionStrategy();
@@ -602,7 +585,7 @@
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
@@ -613,12 +596,45 @@
     EXPECT_TRUE(state.usesDeviceComposition);
 }
 
+TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithDisplayBrightness) {
+    // Since two calls are made to anyLayersRequireClientComposition with different return
+    // values, use a Sequence to control the matching so the values are returned in a known
+    // order.
+    constexpr float kDisplayBrightness = 0.5f;
+    Sequence s;
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(true));
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer,
+                setDisplayBrightness(DEFAULT_DISPLAY_ID, kDisplayBrightness,
+                                     Hwc2::Composer::DisplayBrightnessOptions{.applyImmediately =
+                                                                                      false}))
+            .WillOnce(Return(ByMove(ftl::yield<status_t>(NO_ERROR))));
+
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+
+    mDisplay->setNextBrightness(kDisplayBrightness);
+    mDisplay->chooseCompositionStrategy();
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.usesClientComposition);
+    EXPECT_TRUE(state.usesDeviceComposition);
+    EXPECT_FALSE(state.displayBrightness.has_value());
+}
+
 TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
     android::HWComposer::DeviceRequestedChanges changes{
-            {{nullptr, hal::Composition::CLIENT}},
+            {{nullptr, Composition::CLIENT}},
             hal::DisplayRequest::FLIP_CLIENT_TARGET,
             {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
             {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+            -1.f,
     };
 
     // Since two calls are made to anyLayersRequireClientComposition with different return
@@ -633,8 +649,8 @@
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(changes), Return(NO_ERROR)));
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+            .WillOnce(DoAll(SetArgPointee<5>(changes), Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
     EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
     EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
@@ -652,6 +668,7 @@
  */
 
 using DisplayGetSkipColorTransformTest = DisplayWithLayersTestCommon;
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
 
 TEST_F(DisplayGetSkipColorTransformTest, checksCapabilityIfGpuDisplay) {
     EXPECT_CALL(mHwComposer, hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM))
@@ -664,7 +681,7 @@
 TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
     EXPECT_CALL(mHwComposer,
                 hasDisplayCapability(HalDisplayId(DEFAULT_DISPLAY_ID),
-                                     hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
+                                     DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
             .WillOnce(Return(true));
     EXPECT_TRUE(mDisplay->getSkipColorTransform());
 }
@@ -722,17 +739,15 @@
 }
 
 TEST_F(DisplayApplyChangedTypesToLayersTest, appliesChanges) {
-    EXPECT_CALL(*mLayer1.outputLayer,
-                applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
+    EXPECT_CALL(*mLayer1.outputLayer, applyDeviceCompositionTypeChange(Composition::CLIENT))
             .Times(1);
-    EXPECT_CALL(*mLayer2.outputLayer,
-                applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE))
+    EXPECT_CALL(*mLayer2.outputLayer, applyDeviceCompositionTypeChange(Composition::DEVICE))
             .Times(1);
 
     mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{
-            {&mLayer1.hwc2Layer, hal::Composition::CLIENT},
-            {&mLayer2.hwc2Layer, hal::Composition::DEVICE},
-            {&hwc2LayerUnknown, hal::Composition::SOLID_COLOR},
+            {&mLayer1.hwc2Layer, Composition::CLIENT},
+            {&mLayer2.hwc2Layer, Composition::DEVICE},
+            {&hwc2LayerUnknown, Composition::SOLID_COLOR},
     });
 }
 
@@ -811,15 +826,18 @@
             .dataspace = hal::Dataspace::STANDARD_BT470M,
     };
 
+    static constexpr float kWhitePointNits = 800.f;
+
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
 
     EXPECT_CALL(*renderSurface, setBufferPixelFormat(clientTargetProperty.pixelFormat));
     EXPECT_CALL(*renderSurface, setBufferDataspace(clientTargetProperty.dataspace));
-    mDisplay->applyClientTargetRequests(clientTargetProperty);
+    mDisplay->applyClientTargetRequests(clientTargetProperty, kWhitePointNits);
 
     auto& state = mDisplay->getState();
     EXPECT_EQ(clientTargetProperty.dataspace, state.dataspace);
+    EXPECT_EQ(kWhitePointNits, state.clientTargetWhitePointNits);
 }
 
 /*
@@ -899,13 +917,10 @@
 
     mDisplay->editState().isEnabled = true;
     mDisplay->editState().usesClientComposition = false;
-    mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+    mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = false;
-
-    mDisplay->finishFrame(refreshArgs);
+    mDisplay->finishFrame({});
 }
 
 TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -920,13 +935,10 @@
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
-    gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+    gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = false;
-
-    gpuDisplay->finishFrame(refreshArgs);
+    gpuDisplay->finishFrame({});
 }
 
 TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
@@ -941,34 +953,9 @@
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
-    gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+    gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = false;
-
-    gpuDisplay->finishFrame(refreshArgs);
-}
-
-TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) {
-    auto args = getDisplayCreationArgsForGpuVirtualDisplay();
-    std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
-
-    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
-    gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
-
-    // We expect a single call to queueBuffer when composition is not skipped.
-    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
-
-    gpuDisplay->editState().isEnabled = true;
-    gpuDisplay->editState().usesClientComposition = false;
-    gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
-    gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = true;
-
-    gpuDisplay->finishFrame(refreshArgs);
+    gpuDisplay->finishFrame({});
 }
 
 /*
@@ -998,10 +985,8 @@
             Display>(mCompositionEngine,
                      DisplayCreationArgsBuilder()
                              .setId(DEFAULT_DISPLAY_ID)
-                             .setConnectionType(ui::DisplayConnectionType::Internal)
                              .setPixels(DEFAULT_RESOLUTION)
                              .setIsSecure(true)
-                             .setLayerStackId(DEFAULT_LAYER_STACK)
                              .setPowerAdvisor(&mPowerAdvisor)
                              .build());
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 9518659..5185ea9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -32,6 +32,8 @@
 #include <ui/GraphicTypes.h>
 #include "DisplayHardware/HWC2.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
@@ -56,8 +58,9 @@
                        const android::sp<android::Fence>&));
     MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
     MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
-    MOCK_METHOD1(setColor, Error(hal::Color));
-    MOCK_METHOD1(setCompositionType, Error(hal::Composition));
+    MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color));
+    MOCK_METHOD1(setCompositionType,
+                 Error(aidl::android::hardware::graphics::composer3::Composition));
     MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
     MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
     MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
@@ -71,6 +74,8 @@
     MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
     MOCK_METHOD3(setLayerGenericMetadata,
                  Error(const std::string&, bool, const std::vector<uint8_t>&));
+    MOCK_METHOD1(setWhitePointNits, Error(float));
+    MOCK_METHOD1(setBlockingRegion, Error(const android::Region&));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index ada5adf..dc5c5c8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -43,16 +43,19 @@
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
                        bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
     MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
-    MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability));
+    MOCK_CONST_METHOD2(hasDisplayCapability,
+                       bool(HalDisplayId,
+                            aidl::android::hardware::graphics::composer3::DisplayCapability));
 
     MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
     MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
     MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
+
     MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
-    MOCK_METHOD5(getDeviceCompositionChanges,
+    MOCK_METHOD6(getDeviceCompositionChanges,
                  status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
-                          const std::shared_ptr<FenceTime>&,
+                          const std::shared_ptr<FenceTime>&, nsecs_t,
                           std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
                  status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
@@ -80,7 +83,9 @@
     MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
     MOCK_METHOD4(getDisplayedContentSample,
                  status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(PhysicalDisplayId, float));
+    MOCK_METHOD3(setDisplayBrightness,
+                 std::future<status_t>(PhysicalDisplayId, float,
+                                       const Hwc2::Composer::DisplayBrightnessOptions&));
     MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
 
     MOCK_METHOD2(onHotplug,
@@ -110,11 +115,15 @@
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
-    MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
-    MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
-    MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
-    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId));
-    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId));
+
+    MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
+    MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
+    MOCK_METHOD(bool, isHeadless, (), (const, override));
+
+    MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
+                (const, override));
+    MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
+                (const, override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index fc8cb50..50adcfb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,11 +29,20 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
-    MOCK_METHOD0(init, void());
-    MOCK_METHOD0(onBootFinished, void());
-    MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
-    MOCK_METHOD0(isUsingExpensiveRendering, bool());
-    MOCK_METHOD0(notifyDisplayUpdateImminent, void());
+    MOCK_METHOD(void, init, (), (override));
+    MOCK_METHOD(void, onBootFinished, (), (override));
+    MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
+                (override));
+    MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+    MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(bool, usePowerHintSession, (), (override));
+    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
+    MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
+                (override));
+    MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
+    MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index c8c6012..f34b621 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -30,6 +30,10 @@
 #include "MockHWComposer.h"
 #include "RegionMatcher.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
 namespace android::compositionengine {
 namespace {
 
@@ -158,7 +162,7 @@
         mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
         mLayerFEState.geomBufferTransform = TR_IDENT;
 
-        mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+        mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
     }
 
     FloatRect calculateOutputSourceCrop() {
@@ -229,7 +233,7 @@
 
         mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.displaySpace.orientation = toRotation(entry.display);
+        mOutputState.displaySpace.setOrientation(toRotation(entry.display));
 
         EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
     }
@@ -243,7 +247,7 @@
 }
 
 TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
-    mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+    mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
 
     const FloatRect expected{0.f, 0.f, 960.f, 540.f};
     EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -265,7 +269,7 @@
         mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
         mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
 
-        mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+        mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
         mOutputState.transform = ui::Transform{TR_IDENT};
     }
 
@@ -313,7 +317,7 @@
 }
 
 TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
-    mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+    mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
     const Rect expected{0, 0, 960, 540};
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
@@ -399,7 +403,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.displaySpace.orientation = toRotation(entry.display);
+        mOutputState.displaySpace.setOrientation(toRotation(entry.display));
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -511,7 +515,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.displaySpace.orientation = toRotation(entry.display);
+        mOutputState.displaySpace.setOrientation(toRotation(entry.display));
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -652,6 +656,22 @@
     EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
 }
 
+TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) {
+    mOutputState.sdrWhitePointNits = 200.f;
+    mOutputState.displayBrightnessNits = 800.f;
+
+    mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
+    mLayerFEState.isColorspaceAgnostic = false;
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+    EXPECT_EQ(mOutputState.sdrWhitePointNits, mOutputLayer.getState().whitePointNits);
+
+    mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
+    mLayerFEState.isColorspaceAgnostic = false;
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+}
+
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
     mOutputLayer.editState().forceClientComposition = false;
 
@@ -728,6 +748,8 @@
     static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
     static constexpr bool kLayerGenericMetadata1Mandatory = true;
     static constexpr bool kLayerGenericMetadata2Mandatory = true;
+    static constexpr float kWhitePointNits = 200.f;
+    static constexpr float kDisplayBrightnessNits = 400.f;
 
     static const half4 kColor;
     static const Rect kDisplayFrame;
@@ -758,6 +780,7 @@
         outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
         outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
         outputLayerState.dataspace = kDataspace;
+        outputLayerState.whitePointNits = kWhitePointNits;
 
         mLayerFEState.blendMode = kBlendMode;
         mLayerFEState.alpha = kAlpha;
@@ -770,6 +793,8 @@
         mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
         mLayerFEState.acquireFence = kFence;
 
+        mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
+
         EXPECT_CALL(mOutput, getDisplayColorProfile())
                 .WillRepeatedly(Return(&mDisplayColorProfile));
         EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
@@ -818,9 +843,11 @@
     void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
                                    ui::Dataspace dataspace = kDataspace,
                                    const Region& visibleRegion = kOutputSpaceVisibleRegion,
-                                   const Region& surfaceDamage = kSurfaceDamage) {
+                                   const Region& surfaceDamage = kSurfaceDamage,
+                                   float whitePointNits = kWhitePointNits) {
         EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setWhitePointNits(whitePointNits)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
                 .WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
                                          ? hal::Error::UNSUPPORTED
@@ -828,7 +855,7 @@
         EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
     }
 
-    void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
+    void expectSetCompositionTypeCall(Composition compositionType) {
         EXPECT_CALL(*mHwcLayer, setCompositionType(compositionType)).WillOnce(Return(kError));
     }
 
@@ -837,9 +864,8 @@
     }
 
     void expectSetColorCall() {
-        const hal::Color color = {static_cast<uint8_t>(std::round(kColor.r * 255)),
-                                  static_cast<uint8_t>(std::round(kColor.g * 255)),
-                                  static_cast<uint8_t>(std::round(kColor.b * 255)), 255};
+        const aidl::android::hardware::graphics::composer3::Color color = {kColor.r, kColor.g,
+                                                                           kColor.b, 1.0f};
 
         EXPECT_CALL(*mHwcLayer, setColor(ColorEq(color))).WillOnce(Return(kError));
     }
@@ -876,7 +902,7 @@
                                                    84.f / 255.f};
 const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
 const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
-const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{0.f, 0.f, 4.f, 5.f};
+const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{1002, 1003, 1004, 20044};
 const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
         Rect{1005, 1006, 1007, 1008}};
 const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
@@ -942,7 +968,7 @@
     // This test simulates a scenario where displayInstallOrientation is set to
     // ROT_90. This only has an effect on the transform; orientation stays 0 (see
     // DisplayDevice::setProjection).
-    mOutputState.displaySpace.orientation = ui::ROTATION_0;
+    mOutputState.displaySpace.setOrientation(ui::ROTATION_0);
     mOutputState.transform = ui::Transform{TR_ROT_90};
     // Buffers are pre-rotated based on the transform hint (ROT_90); their
     // geomBufferTransform is set to the inverse transform.
@@ -952,7 +978,7 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -961,7 +987,7 @@
     // check this in this test only by setting up an testing::InSeqeuence
     // instance before setting up the two expectations.
     InSequence s;
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
+    expectSetCompositionTypeCall(Composition::SOLID_COLOR);
     expectSetColorCall();
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
@@ -969,11 +995,11 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+    mLayerFEState.compositionType = Composition::SIDEBAND;
 
     expectPerFrameCommonCalls();
     expectSetSidebandHandleCall();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
+    expectSetCompositionTypeCall(Composition::SIDEBAND);
 
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
@@ -982,11 +1008,11 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
+    mLayerFEState.compositionType = Composition::CURSOR;
 
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
+    expectSetCompositionTypeCall(Composition::CURSOR);
 
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
@@ -995,11 +1021,11 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
 
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
 
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
@@ -1008,10 +1034,9 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
-    (*mOutputLayer.editState().hwc).hwcCompositionType =
-            Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    (*mOutputLayer.editState().hwc).hwcCompositionType = Composition::SOLID_COLOR;
 
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
     expectSetColorCall();
@@ -1024,11 +1049,11 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform);
     expectSetColorCall();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+    expectSetCompositionTypeCall(Composition::CLIENT);
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1037,25 +1062,25 @@
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
     mOutputLayer.editState().forceClientComposition = true;
 
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
     expectSetColorCall();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+    expectSetCompositionTypeCall(Composition::CLIENT);
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     includeGenericLayerMetadataInState();
 
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
     expectGenericLayerMetadataCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
 
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
@@ -1064,12 +1089,12 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     includeGenericLayerMetadataInState();
 
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
 
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
@@ -1078,15 +1103,31 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     includeOverrideInfo();
 
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kSkipAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage);
+                              kOverrideSurfaceDamage, kDisplayBrightnessNits);
     expectSetHdrMetadataAndBufferCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSendBuffer) {
+    mLayerFEState.compositionType = Composition::SOLID_COLOR;
+    includeOverrideInfo();
+
+    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+                              kOverrideBlendMode, kSkipAlpha);
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+                              kOverrideSurfaceDamage, kDisplayBrightnessNits);
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
@@ -1094,15 +1135,31 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     includeOverrideInfo();
 
     expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                               kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
-                              kOverrideSurfaceDamage);
+                              kOverrideSurfaceDamage, kDisplayBrightnessNits);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) {
+    mLayerFEState.compositionType = Composition::SOLID_COLOR;
+    includeOverrideInfo();
+
+    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+                              kOverrideBlendMode, kOverrideAlpha);
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+                              kOverrideSurfaceDamage, kDisplayBrightnessNits);
+    expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
+    expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1110,14 +1167,14 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     mOutputLayer.editState().hwc->stateOverridden = true;
 
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
                               Region::INVALID_REGION);
     expectSetHdrMetadataAndBufferCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1125,16 +1182,16 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     mOutputLayer.editState().hwc->stateOverridden = true;
     mOutputLayer.editState().hwc->layerSkipped = true;
-    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
 
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
                               Region::INVALID_REGION);
     expectSetHdrMetadataAndBufferCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1142,17 +1199,17 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     mOutputLayer.editState().forceClientComposition = true;
     mOutputLayer.editState().hwc->stateOverridden = true;
     mOutputLayer.editState().hwc->layerSkipped = true;
-    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+    mOutputLayer.editState().hwc->hwcCompositionType = Composition::CLIENT;
 
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
                               Region::INVALID_REGION);
     expectSetHdrMetadataAndBufferCalls();
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+    expectSetCompositionTypeCall(Composition::CLIENT);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1198,7 +1255,7 @@
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls();
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+    expectSetCompositionTypeCall(Composition::CLIENT);
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/
@@ -1210,14 +1267,13 @@
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
-    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    expectSetCompositionTypeCall(Composition::DEVICE);
 
-    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Composition::DEVICE;
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/
                                  true);
-    EXPECT_EQ(Hwc2::IComposerClient::Composition::DEVICE,
-              mOutputLayer.getState().hwc->hwcCompositionType);
+    EXPECT_EQ(Composition::DEVICE, mOutputLayer.getState().hwc->hwcCompositionType);
 }
 
 /*
@@ -1237,7 +1293,7 @@
 
         mLayerFEState.cursorFrame = kDefaultCursorFrame;
 
-        mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
+        mOutputState.layerStackSpace.setContent(kDefaultDisplayViewport);
         mOutputState.transform = ui::Transform{kDefaultTransform};
     }
 
@@ -1316,14 +1372,14 @@
 
 TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfSetToClientComposition) {
     mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
-    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+    mOutputLayer.editState().hwc->hwcCompositionType = Composition::CLIENT;
 
     EXPECT_TRUE(mOutputLayer.requiresClientComposition());
 }
 
 TEST_F(OutputLayerTest, requiresClientCompositionReturnsFalseIfSetToDeviceComposition) {
     mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
-    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
 
     EXPECT_FALSE(mOutputLayer.requiresClientComposition());
 }
@@ -1340,14 +1396,14 @@
 
 TEST_F(OutputLayerTest, isHardwareCursorReturnsTrueIfSetToCursorComposition) {
     mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
-    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CURSOR;
+    mOutputLayer.editState().hwc->hwcCompositionType = Composition::CURSOR;
 
     EXPECT_TRUE(mOutputLayer.isHardwareCursor());
 }
 
 TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfSetToDeviceComposition) {
     mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
-    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
 
     EXPECT_FALSE(mOutputLayer.isHardwareCursor());
 }
@@ -1358,13 +1414,12 @@
 
 TEST_F(OutputLayerTest, applyDeviceCompositionTypeChangeSetsNewType) {
     mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
-    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
 
-    mOutputLayer.applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT);
+    mOutputLayer.applyDeviceCompositionTypeChange(Composition::CLIENT);
 
     ASSERT_TRUE(mOutputLayer.getState().hwc);
-    EXPECT_EQ(Hwc2::IComposerClient::Composition::CLIENT,
-              mOutputLayer.getState().hwc->hwcCompositionType);
+    EXPECT_EQ(Composition::CLIENT, mOutputLayer.getState().hwc->hwcCompositionType);
 }
 
 /*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 09f5a5e..f7c7533 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -24,6 +24,7 @@
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/OutputLayer.h>
 #include <compositionengine/mock/RenderSurface.h>
+#include <ftl/future.h>
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/Rect.h>
@@ -35,6 +36,7 @@
 #include "CallOrderStateMachineHelper.h"
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
+#include "TestUtils.h"
 #include "renderengine/ExternalTexture.h"
 
 namespace android::compositionengine {
@@ -142,7 +144,8 @@
                 std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
         mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
 
-        mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
+        mOutput->editState().displaySpace.setBounds(
+                ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
     }
 
@@ -288,8 +291,10 @@
 
 TEST_F(OutputTest, setProjectionWorks) {
     const Rect displayRect{0, 0, 1000, 2000};
-    mOutput->editState().displaySpace.bounds = displayRect;
-    mOutput->editState().framebufferSpace.bounds = displayRect;
+    mOutput->editState().displaySpace.setBounds(
+            ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+    mOutput->editState().framebufferSpace.setBounds(
+            ui::Size(displayRect.getWidth(), displayRect.getHeight()));
 
     const ui::Rotation orientation = ui::ROTATION_90;
     const Rect frame{50, 60, 100, 100};
@@ -297,28 +302,29 @@
 
     mOutput->setProjection(orientation, viewport, frame);
 
-    EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
-    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
-    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+    EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
 
     const auto state = mOutput->getState();
-    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
-    EXPECT_EQ(viewport, state.layerStackSpace.content);
-    EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+    EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
 
-    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
-    EXPECT_EQ(frame, state.orientedDisplaySpace.content);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+    EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
 
-    EXPECT_EQ(displayRect, state.displaySpace.bounds);
-    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
-    EXPECT_EQ(orientation, state.displaySpace.orientation);
+    EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+    EXPECT_EQ(orientation, state.displaySpace.getOrientation());
 
-    EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
-    EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
-    EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+    EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.getContent());
+    EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
 
-    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+    EXPECT_EQ(state.displaySpace.getContent(),
+              state.transform.transform(state.layerStackSpace.getContent()));
 
     EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint());
 }
@@ -326,8 +332,10 @@
 TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
     const Rect displayRect{0, 0, 1000, 2000};
     const Rect framebufferRect{0, 0, 500, 1000};
-    mOutput->editState().displaySpace.bounds = displayRect;
-    mOutput->editState().framebufferSpace.bounds = framebufferRect;
+    mOutput->editState().displaySpace.setBounds(
+            ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+    mOutput->editState().framebufferSpace.setBounds(
+            ui::Size(framebufferRect.getWidth(), framebufferRect.getHeight()));
 
     const ui::Rotation orientation = ui::ROTATION_90;
     const Rect frame{50, 60, 100, 100};
@@ -335,28 +343,29 @@
 
     mOutput->setProjection(orientation, viewport, frame);
 
-    EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
-    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
-    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+    EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
 
     const auto state = mOutput->getState();
-    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
-    EXPECT_EQ(viewport, state.layerStackSpace.content);
-    EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+    EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
 
-    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
-    EXPECT_EQ(frame, state.orientedDisplaySpace.content);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+    EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
 
-    EXPECT_EQ(displayRect, state.displaySpace.bounds);
-    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
-    EXPECT_EQ(orientation, state.displaySpace.orientation);
+    EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+    EXPECT_EQ(orientation, state.displaySpace.getOrientation());
 
-    EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
-    EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
-    EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+    EXPECT_EQ(framebufferRect, state.framebufferSpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.getContent());
+    EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
 
-    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+    EXPECT_EQ(state.displaySpace.getContent(),
+              state.transform.transform(state.layerStackSpace.getContent()));
 }
 
 /*
@@ -364,16 +373,16 @@
  */
 
 TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
-    mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
-    mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
-    mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
-    mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
-    mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
-    mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
-    mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
-    mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
-    mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
-    mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
+    mOutput->editState().layerStackSpace.setContent(Rect(0, 0, 2000, 1000));
+    mOutput->editState().layerStackSpace.setBounds(ui::Size(2000, 1000));
+    mOutput->editState().orientedDisplaySpace.setContent(Rect(0, 0, 1800, 900));
+    mOutput->editState().orientedDisplaySpace.setBounds(ui::Size(2000, 1000));
+    mOutput->editState().framebufferSpace.setContent(Rect(0, 0, 900, 1800));
+    mOutput->editState().framebufferSpace.setBounds(ui::Size(1000, 2000));
+    mOutput->editState().framebufferSpace.setOrientation(ui::ROTATION_90);
+    mOutput->editState().displaySpace.setContent(Rect(0, 0, 900, 1800));
+    mOutput->editState().displaySpace.setBounds(ui::Size(1000, 2000));
+    mOutput->editState().displaySpace.setOrientation(ui::ROTATION_90);
 
     const ui::Size newDisplaySize{500, 1000};
 
@@ -384,36 +393,38 @@
     const auto state = mOutput->getState();
 
     const Rect displayRect(newDisplaySize);
-    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getBoundsAsRect());
 
-    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
-    EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+    EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.getBoundsAsRect());
 
-    EXPECT_EQ(displayRect, state.displaySpace.bounds);
-    EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+    EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+    EXPECT_EQ(ui::ROTATION_90, state.displaySpace.getOrientation());
 
-    EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
-    EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+    EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+    EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.getOrientation());
 
-    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+    EXPECT_EQ(state.displaySpace.getContent(),
+              state.transform.transform(state.layerStackSpace.getContent()));
 
     EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
 }
 
 /*
- * Output::setLayerStackFilter()
+ * Output::setLayerFilter()
  */
 
-TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
-    const uint32_t layerStack = 123u;
-    mOutput->setLayerStackFilter(layerStack, true);
+TEST_F(OutputTest, setLayerFilterSetsFilterAndDirtiesEntireOutput) {
+    constexpr ui::LayerFilter kFilter{ui::LayerStack{123u}, true};
+    mOutput->setLayerFilter(kFilter);
 
-    EXPECT_TRUE(mOutput->getState().layerStackInternal);
-    EXPECT_EQ(layerStack, mOutput->getState().layerStackId);
+    const auto& state = mOutput->getState();
+    EXPECT_EQ(kFilter.layerStack, state.layerFilter.layerStack);
+    EXPECT_TRUE(state.layerFilter.toInternalDisplay);
 
-    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_THAT(state.dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
 /*
@@ -560,133 +571,118 @@
 
     mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
 
-    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
+    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.getBoundsAsRect());
+}
+
+/**
+ * Output::setDisplayBrightness()
+ */
+
+TEST_F(OutputTest, setNextBrightness) {
+    constexpr float kDisplayBrightness = 0.5f;
+    mOutput->setNextBrightness(kDisplayBrightness);
+    ASSERT_TRUE(mOutput->getState().displayBrightness.has_value());
+    EXPECT_EQ(kDisplayBrightness, mOutput->getState().displayBrightness);
 }
 
 /*
  * Output::getDirtyRegion()
  */
 
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
+TEST_F(OutputTest, getDirtyRegion) {
     const Rect viewport{100, 200};
-    mOutput->editState().layerStackSpace.content = viewport;
+    mOutput->editState().layerStackSpace.setContent(viewport);
     mOutput->editState().dirtyRegion.set(50, 300);
 
-    {
-        Region result = mOutput->getDirtyRegion(true);
-
-        EXPECT_THAT(result, RegionEq(Region(viewport)));
-    }
-}
-
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
-    const Rect viewport{100, 200};
-    mOutput->editState().layerStackSpace.content = viewport;
-    mOutput->editState().dirtyRegion.set(50, 300);
-
-    {
-        Region result = mOutput->getDirtyRegion(false);
-
-        // The dirtyRegion should be clipped to the display bounds.
-        EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
-    }
+    // The dirty region should be clipped to the display bounds.
+    EXPECT_THAT(mOutput->getDirtyRegion(), RegionEq(Region(Rect(50, 200))));
 }
 
 /*
- * Output::belongsInOutput()
+ * Output::includesLayer()
  */
 
-TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
-    const uint32_t layerStack1 = 123u;
-    const uint32_t layerStack2 = 456u;
+TEST_F(OutputTest, layerFiltering) {
+    const ui::LayerStack layerStack1{123u};
+    const ui::LayerStack layerStack2{456u};
 
-    // If the output accepts layerStack1 and internal-only layers....
-    mOutput->setLayerStackFilter(layerStack1, true);
+    // If the output is associated to layerStack1 and to an internal display...
+    mOutput->setLayerFilter({layerStack1, true});
 
-    // A layer with no layerStack does not belong to it, internal-only or not.
-    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false));
-    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true));
+    // It excludes layers with no layer stack, internal-only or not.
+    EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, false}));
+    EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, true}));
 
-    // Any layer with layerStack1 belongs to it, internal-only or not.
-    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
-    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
+    // It includes layers on layerStack1, internal-only or not.
+    EXPECT_TRUE(mOutput->includesLayer({layerStack1, false}));
+    EXPECT_TRUE(mOutput->includesLayer({layerStack1, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, false}));
 
-    // If the output accepts layerStack21 but not internal-only layers...
-    mOutput->setLayerStackFilter(layerStack1, false);
+    // If the output is associated to layerStack1 but not to an internal display...
+    mOutput->setLayerFilter({layerStack1, false});
 
-    // Only non-internal layers with layerStack1 belong to it.
-    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
+    // It includes layers on layerStack1, unless they are internal-only.
+    EXPECT_TRUE(mOutput->includesLayer({layerStack1, false}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack1, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, false}));
 }
 
-TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) {
+TEST_F(OutputTest, layerFilteringWithoutCompositionState) {
     NonInjectedLayer layer;
     sp<LayerFE> layerFE(layer.layerFE);
 
-    // If the layer has no composition state, it does not belong to any output.
+    // Layers without composition state are excluded.
     EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 }
 
-TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
+TEST_F(OutputTest, layerFilteringWithCompositionState) {
     NonInjectedLayer layer;
     sp<LayerFE> layerFE(layer.layerFE);
 
-    const uint32_t layerStack1 = 123u;
-    const uint32_t layerStack2 = 456u;
+    const ui::LayerStack layerStack1{123u};
+    const ui::LayerStack layerStack2{456u};
 
-    // If the output accepts layerStack1 and internal-only layers....
-    mOutput->setLayerStackFilter(layerStack1, true);
+    // If the output is associated to layerStack1 and to an internal display...
+    mOutput->setLayerFilter({layerStack1, true});
 
-    // A layer with no layerStack does not belong to it, internal-only or not.
-    layer.layerFEState.layerStackId = std::nullopt;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    // It excludes layers with no layer stack, internal-only or not.
+    layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, false};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = std::nullopt;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    // Any layer with layerStack1 belongs to it, internal-only or not.
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+    // It includes layers on layerStack1, internal-only or not.
+    layer.layerFEState.outputFilter = {layerStack1, false};
+    EXPECT_TRUE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack1, true};
+    EXPECT_TRUE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, false};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    // If the output accepts layerStack1 but not internal-only layers...
-    mOutput->setLayerStackFilter(layerStack1, false);
+    // If the output is associated to layerStack1 but not to an internal display...
+    mOutput->setLayerFilter({layerStack1, false});
 
-    // Only non-internal layers with layerStack1 belong to it.
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+    // It includes layers on layerStack1, unless they are internal-only.
+    layer.layerFEState.outputFilter = {layerStack1, false};
+    EXPECT_TRUE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack1, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, false};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 }
 
 /*
@@ -1079,7 +1075,8 @@
     OutputRebuildLayerStacksTest() {
         mOutput.mState.isEnabled = true;
         mOutput.mState.transform = kIdentityTransform;
-        mOutput.mState.displaySpace.bounds = kOutputBounds;
+        mOutput.mState.displaySpace.setBounds(
+                ui::Size(kOutputBounds.getWidth(), kOutputBounds.getHeight()));
 
         mRefreshArgs.updatingOutputGeometryThisFrame = true;
 
@@ -1268,21 +1265,22 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+        MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&),
+                    (const, override));
         MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
         MOCK_METHOD2(ensureOutputLayer,
                      compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
     };
 
     OutputEnsureOutputLayerIfVisibleTest() {
-        EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE)))
+        EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE)))
                 .WillRepeatedly(Return(true));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
 
-        mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
-        mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
+        mOutput.mState.displaySpace.setBounds(ui::Size(200, 300));
+        mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 200, 300));
         mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
 
         mLayer.layerFEState.isVisible = true;
@@ -1326,8 +1324,8 @@
 const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
         Region(Rect(0, 0, 200, 100));
 
-TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
-    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
+    EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
     EXPECT_CALL(*mLayer.layerFE,
                 prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
 
@@ -1337,8 +1335,8 @@
 }
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest,
-       skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
-    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+       skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerIncluded) {
+    EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
 
     ensureOutputLayerIfVisible();
 }
@@ -1362,7 +1360,7 @@
 }
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
-    mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
+    mOutput.mState.displaySpace.setBounds(ui::Size(0, 0));
 
     ensureOutputLayerIfVisible();
 }
@@ -1559,7 +1557,7 @@
     mLayer.layerFEState.contentDirty = true;
     mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
 
-    mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+    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));
@@ -1585,7 +1583,7 @@
     mLayer.layerFEState.contentDirty = true;
     mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
 
-    mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+    mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
     mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
 
     EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -2530,7 +2528,7 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+        MOCK_METHOD(Region, getDirtyRegion, (), (const));
     };
 
     OutputBeginFrameTest() {
@@ -2542,8 +2540,7 @@
     struct IfGetDirtyRegionExpectationState
           : public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> {
         [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) {
-            EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false))
-                    .WillOnce(Return(dirtyRegion));
+            EXPECT_CALL(getInstance()->mOutput, getDirtyRegion()).WillOnce(Return(dirtyRegion));
             return nextState<AndIfGetOutputLayerCountExpectationState>();
         }
     };
@@ -2683,7 +2680,7 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+        MOCK_METHOD(Region, getDirtyRegion, (), (const));
         MOCK_METHOD2(composeSurfaces,
                      std::optional<base::unique_fd>(
                              const Region&, const compositionengine::CompositionRefreshArgs&));
@@ -2711,7 +2708,6 @@
 
 TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = {};
-    mRefreshArgs.repaintEverything = true;
     mOutput.mState.isEnabled = true;
 
     mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2719,7 +2715,6 @@
 
 TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
-    mRefreshArgs.repaintEverything = true;
     mOutput.mState.isEnabled = false;
 
     InSequence seq;
@@ -2729,13 +2724,12 @@
     mOutput.devOptRepaintFlash(mRefreshArgs);
 }
 
-TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) {
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfEnabled) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
-    mRefreshArgs.repaintEverything = true;
     mOutput.mState.isEnabled = true;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion));
+    EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, prepareFrame());
 
@@ -2744,11 +2738,10 @@
 
 TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
-    mRefreshArgs.repaintEverything = false;
     mOutput.mState.isEnabled = true;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
+    EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
     EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
     EXPECT_CALL(mOutput, postFramebuffer());
@@ -2903,12 +2896,24 @@
     // are passed. This happens to work with the current implementation, but
     // would not survive certain calls like Fence::merge() which would return a
     // new instance.
-    EXPECT_CALL(*mLayer1.layerFE,
-                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
-    EXPECT_CALL(*mLayer2.layerFE,
-                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
-    EXPECT_CALL(*mLayer3.layerFE,
-                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
+    base::unique_fd layer1FD(layer1Fence->dup());
+    base::unique_fd layer2FD(layer2Fence->dup());
+    base::unique_fd layer3FD(layer3Fence->dup());
+    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_))
+            .WillOnce([&layer1FD](std::shared_future<renderengine::RenderEngineResult>
+                                          futureRenderEngineResult) {
+                EXPECT_EQ(layer1FD, futureRenderEngineResult.get().drawFence);
+            });
+    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_))
+            .WillOnce([&layer2FD](std::shared_future<renderengine::RenderEngineResult>
+                                          futureRenderEngineResult) {
+                EXPECT_EQ(layer2FD, futureRenderEngineResult.get().drawFence);
+            });
+    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_))
+            .WillOnce([&layer3FD](std::shared_future<renderengine::RenderEngineResult>
+                                          futureRenderEngineResult) {
+                EXPECT_EQ(layer3FD, futureRenderEngineResult.get().drawFence);
+            });
 
     mOutput.postFramebuffer();
 }
@@ -2934,9 +2939,9 @@
     // Fence::merge is called, and since none of the fences are actually valid,
     // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
     // This is the best we can do without creating a real kernel fence object.
-    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
-    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
-    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed).WillOnce(Return());
+    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
+    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
 
     mOutput.postFramebuffer();
 }
@@ -2968,12 +2973,22 @@
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
     // Each released layer should be given the presentFence.
-    EXPECT_CALL(*releasedLayer1,
-                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
-    EXPECT_CALL(*releasedLayer2,
-                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
-    EXPECT_CALL(*releasedLayer3,
-                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+    base::unique_fd layerFD(presentFence.get()->dup());
+    EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_))
+            .WillOnce([&layerFD](std::shared_future<renderengine::RenderEngineResult>
+                                         futureRenderEngineResult) {
+                EXPECT_EQ(layerFD, futureRenderEngineResult.get().drawFence);
+            });
+    EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_))
+            .WillOnce([&layerFD](std::shared_future<renderengine::RenderEngineResult>
+                                         futureRenderEngineResult) {
+                EXPECT_EQ(layerFD, futureRenderEngineResult.get().drawFence);
+            });
+    EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_))
+            .WillOnce([&layerFD](std::shared_future<renderengine::RenderEngineResult>
+                                         futureRenderEngineResult) {
+                EXPECT_EQ(layerFD, futureRenderEngineResult.get().drawFence);
+            });
 
     mOutput.postFramebuffer();
 
@@ -2993,7 +3008,7 @@
         // mock implementations.
         MOCK_CONST_METHOD0(getSkipColorTransform, bool());
         MOCK_METHOD3(generateClientCompositionRequests,
-                     std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+                     std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<LayerFE*>&));
         MOCK_METHOD2(appendRegionFlashRequests,
                      void(const Region&, std::vector<LayerFE::LayerSettings>&));
         MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -3005,11 +3020,11 @@
         mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
         mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
 
-        mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
-        mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
-        mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
-        mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
-        mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+        mOutput.mState.orientedDisplaySpace.setContent(kDefaultOutputFrame);
+        mOutput.mState.layerStackSpace.setContent(kDefaultOutputViewport);
+        mOutput.mState.framebufferSpace.setContent(kDefaultOutputDestinationClip);
+        mOutput.mState.displaySpace.setContent(kDefaultOutputDestinationClip);
+        mOutput.mState.displaySpace.setOrientation(kDefaultOutputOrientation);
         mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
         mOutput.mState.dataspace = kDefaultOutputDataspace;
         mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
@@ -3019,6 +3034,7 @@
         mOutput.mState.usesDeviceComposition = false;
         mOutput.mState.reusedClientComposition = false;
         mOutput.mState.flipClientTarget = false;
+        mOutput.mState.clientTargetWhitePointNits = kClientTargetLuminanceNits;
 
         EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
@@ -3053,6 +3069,7 @@
     static constexpr float kDefaultMaxLuminance = 0.9f;
     static constexpr float kDefaultAvgLuminance = 0.7f;
     static constexpr float kDefaultMinLuminance = 0.1f;
+    static constexpr float kClientTargetLuminanceNits = 200.f;
 
     static const Rect kDefaultOutputFrame;
     static const Rect kDefaultOutputViewport;
@@ -3142,15 +3159,20 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
-
+    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
     verify().execute().expectAFenceWasReturned();
 }
 
@@ -3165,7 +3187,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(
@@ -3175,8 +3197,14 @@
                     }));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
 
     verify().execute().expectAFenceWasReturned();
 }
@@ -3188,14 +3216,13 @@
 
     r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
     r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
-    const constexpr uint32_t kInternalLayerStack = 1234;
-    mOutput.setLayerStackFilter(kInternalLayerStack, true);
+    mOutput.setLayerFilter({ui::LayerStack{1234u}, true});
 
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(
@@ -3205,8 +3232,14 @@
                     }));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, true, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
 
     verify().execute().expectAFenceWasReturned();
 }
@@ -3223,15 +3256,18 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
             .Times(2)
-            .WillOnce(Return(NO_ERROR));
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3252,14 +3288,15 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
     verify().execute().expectAFenceWasReturned();
@@ -3281,7 +3318,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
@@ -3293,8 +3330,14 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
             .WillOnce(Return(mOutputBuffer))
             .WillOnce(Return(otherOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3316,17 +3359,19 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _))
-            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3339,7 +3384,7 @@
     OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
         EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
         EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
                 .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
@@ -3375,8 +3420,9 @@
     struct ExpectDisplaySettingsState
           : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
         auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
-            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _))
-                    .WillOnce(Return(NO_ERROR));
+            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _))
+                    .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
+                            {NO_ERROR, base::unique_fd()}))));
             return nextState<ExecuteState>();
         }
     };
@@ -3391,7 +3437,8 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+                                            kDefaultOutputOrientationFlags,
+                                            kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3402,7 +3449,8 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+                                            kDefaultOutputOrientationFlags,
+                                            kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3411,10 +3459,10 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
-                                            kDefaultColorTransformMat, Region::INVALID_REGION,
-                                            kDefaultOutputOrientationFlags})
+            .thenExpectDisplaySettingsUsed(
+                    {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+                     kDefaultOutputDataspace, kDefaultColorTransformMat,
+                     kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3423,10 +3471,10 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(false)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
-                                            kDefaultColorTransformMat, Region::INVALID_REGION,
-                                            kDefaultOutputOrientationFlags})
+            .thenExpectDisplaySettingsUsed(
+                    {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+                     kDefaultOutputDataspace, kDefaultColorTransformMat,
+                     kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3438,7 +3486,8 @@
             .andIfSkipColorTransform(true)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+                                            kDefaultOutputOrientationFlags,
+                                            kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3474,8 +3523,15 @@
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
         EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _))
-                .WillRepeatedly(Return(NO_ERROR));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+                .WillRepeatedly(
+                        [&](const renderengine::DisplaySettings&,
+                            const std::vector<renderengine::LayerSettings>&,
+                            const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                            base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+                            return futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
+                        });
     }
 
     Layer mLayer1;
@@ -3527,7 +3583,9 @@
     EXPECT_CALL(*mRenderSurface, setProtected(true));
     // Must happen after setting the protected content state.
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3590,14 +3648,16 @@
 TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
     mOutput.mState.dataspace = kExpensiveOutputDataspace;
 
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kExpensiveOutputDataspace, _))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
 
     // For this test, we also check the call order of key functions.
     InSequence seq;
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3613,9 +3673,11 @@
         EXPECT_CALL(mLayer.outputLayer,
                     writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                     /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+                .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
+                        {NO_ERROR, base::unique_fd()}))));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
@@ -3652,11 +3714,11 @@
 struct GenerateClientCompositionRequestsTest : public testing::Test {
     struct OutputPartialMock : public OutputPartialMockBase {
         // compositionengine::Output overrides
-        std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
-                bool supportsProtectedContent, Region& clearRegion,
-                ui::Dataspace dataspace) override {
+        std::vector<LayerFE::LayerSettings> generateClientCompositionRequestsHelper(
+            bool supportsProtectedContent, ui::Dataspace dataspace) {
+            std::vector<LayerFE*> ignore;
             return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
-                                                                   clearRegion, dataspace);
+                                                                   dataspace, ignore);
         }
     };
 
@@ -3683,6 +3745,8 @@
         mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
     }
 
+    static constexpr float kLayerWhitePointNits = 200.f;
+
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
     mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
     StrictMock<OutputPartialMock> mOutput;
@@ -3691,12 +3755,12 @@
 struct GenerateClientCompositionRequestsTest_ThreeLayers
       : public GenerateClientCompositionRequestsTest {
     GenerateClientCompositionRequestsTest_ThreeLayers() {
-        mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
-        mOutput.mState.layerStackSpace.content = kDisplayViewport;
-        mOutput.mState.displaySpace.content = kDisplayDestinationClip;
+        mOutput.mState.orientedDisplaySpace.setContent(kDisplayFrame);
+        mOutput.mState.layerStackSpace.setContent(kDisplayViewport);
+        mOutput.mState.displaySpace.setContent(kDisplayDestinationClip);
         mOutput.mState.transform =
                 ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
-        mOutput.mState.displaySpace.orientation = kDisplayOrientation;
+        mOutput.mState.displaySpace.setOrientation(kDisplayOrientation);
         mOutput.mState.needsFiltering = false;
         mOutput.mState.isSecure = false;
 
@@ -3722,6 +3786,7 @@
 
     static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
     static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
+    static constexpr float kLayerWhitePointNits = 200.f;
 
     static const Rect kDisplayFrame;
     static const Rect kDisplayViewport;
@@ -3740,11 +3805,9 @@
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     EXPECT_EQ(0u, requests.size());
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) {
@@ -3752,11 +3815,9 @@
     mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
     mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     EXPECT_EQ(0u, requests.size());
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
@@ -3771,16 +3832,13 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     ASSERT_EQ(3u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
     EXPECT_EQ(mShadowSettings, requests[1]);
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
 
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
-
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
     EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
@@ -3811,16 +3869,13 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     ASSERT_EQ(3u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
     EXPECT_EQ(mShadowSettings, requests[1]);
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
 
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
-
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
     EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
@@ -3844,13 +3899,10 @@
     EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
-
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -3870,13 +3922,10 @@
     EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
-
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) {
@@ -3897,32 +3946,30 @@
     mLayers[0].mLayerFEState.isOpaque = true;
     mLayers[1].mLayerFEState.isOpaque = true;
     mLayers[2].mLayerFEState.isOpaque = true;
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    Region stubRegion;
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
-            false,      /* needs filtering */
-            false,      /* secure */
-            false,      /* supports protected content */
-            stubRegion, /* clear region */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
             kDisplayViewport,
             kDisplayDataspace,
             false /* realContentIsVisible */,
             true /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -3936,16 +3983,14 @@
     EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     ASSERT_EQ(2u, requests.size());
 
     // The second layer is expected to be rendered as alpha=0 black with no blending
     EXPECT_EQ(mBlackoutSettings, requests[0]);
 
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
-
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -3954,43 +3999,41 @@
     mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30));
     mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(Rect(10, 10, 20, 20)),
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(Rect(0, 0, 30, 30)),
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(Rect(0, 0, 40, 201)),
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4001,8 +4044,8 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
-            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                      kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4010,43 +4053,41 @@
     mOutput.mState.needsFiltering = false;
     EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4057,8 +4098,8 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
-            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4066,44 +4107,41 @@
     mOutput.mState.needsFiltering = true;
     EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
-
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4114,51 +4152,49 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
-            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
        wholeOutputSecurityUsedToGenerateRequests) {
     mOutput.mState.isSecure = true;
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4169,49 +4205,47 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
-            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
        protectedContentSupportUsedToGenerateRequests) {
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4221,8 +4255,7 @@
     EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
-    static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
-                                                                accumClearRegion,
+    static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
                                                                 kDisplayDataspace));
 }
 
@@ -4343,11 +4376,11 @@
     const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
     constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
 
-    mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
-    mOutput.mState.layerStackSpace.content = kPortraitViewport;
-    mOutput.mState.displaySpace.content = kPortraitDestinationClip;
+    mOutput.mState.orientedDisplaySpace.setContent(kPortraitFrame);
+    mOutput.mState.layerStackSpace.setContent(kPortraitViewport);
+    mOutput.mState.displaySpace.setContent(kPortraitDestinationClip);
     mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
-    mOutput.mState.displaySpace.orientation = kPortraitOrientation;
+    mOutput.mState.displaySpace.setOrientation(kPortraitOrientation);
     mOutput.mState.needsFiltering = false;
     mOutput.mState.isSecure = true;
 
@@ -4370,19 +4403,17 @@
     EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
             .WillRepeatedly(Return(&rightLayer.mOutputLayer));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
             Region(Rect(0, 0, 1000, 1000)),
             false, /* needs filtering */
             true,  /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kPortraitViewport,
             kOutputDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4395,12 +4426,12 @@
             false, /* needs filtering */
             true,  /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kPortraitViewport,
             kOutputDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4409,8 +4440,8 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
 
     constexpr bool supportsProtectedContent = true;
-    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
-                                                              accumClearRegion, kOutputDataspace);
+    auto requests =
+        mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent, kOutputDataspace);
     ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
     EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
@@ -4423,18 +4454,17 @@
     const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
     const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
             Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
             false,                                                    /* needs filtering */
             false,                                                    /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             false /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     LayerFE::LayerSettings mShadowSettings;
@@ -4448,8 +4478,8 @@
     EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
 
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
 
     EXPECT_EQ(mShadowSettings, requests[0]);
@@ -4469,18 +4499,17 @@
     mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
     mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
             Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
             false,                                                    /* needs filtering */
             false,                                                    /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+            kLayerWhitePointNits,
     };
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -4489,8 +4518,8 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
-    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                              kDisplayDataspace);
     ASSERT_EQ(2u, requests.size());
 
     EXPECT_EQ(mShadowSettings, requests[0]);
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
index 704f5a8..19b3928 100644
--- a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -53,40 +53,40 @@
 
 TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
     ProjectionSpace space;
-    space.content = Rect(100, 200);
-    space.bounds = Rect(100, 200);
+    space.setContent(Rect(100, 200));
+    space.setBounds(ui::Size(100, 200));
 
     const ui::Transform identity;
     for (int rotation = 0; rotation <= 3; rotation++) {
-        space.orientation = ui::Rotation(rotation);
+        space.setOrientation(ui::Rotation(rotation));
         EXPECT_EQ(space.getTransform(space), identity);
     }
 }
 
 TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
     ProjectionSpace source;
-    source.content = Rect(10, 10, 20, 20);
-    source.bounds = Rect(100, 200);
+    source.setContent(Rect(10, 10, 20, 20));
+    source.setBounds(ui::Size(100, 200));
 
     ProjectionSpace dest;
-    dest.content = Rect(10, 20, 30, 20);
-    dest.bounds = source.bounds;
+    dest.setContent(Rect(10, 20, 30, 20));
+    dest.setBounds(source.getBounds());
 
     const auto transform = source.getTransform(dest);
-    EXPECT_EQ(transform.transform(source.content), dest.content);
+    EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
 }
 
 TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
     ProjectionSpace source;
-    source.content = Rect(0, 0, 20, 20);
-    source.bounds = Rect(100, 200);
+    source.setContent(Rect(0, 0, 20, 20));
+    source.setBounds(ui::Size(100, 200));
 
     ProjectionSpace dest;
-    dest.content = Rect(0, 0, 40, 30);
-    dest.bounds = source.bounds;
+    dest.setContent(Rect(0, 0, 40, 30));
+    dest.setBounds(source.getBounds());
 
     const auto transform = source.getTransform(dest);
-    EXPECT_EQ(transform.transform(source.content), dest.content);
+    EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
 }
 
 TEST(ProjectionSpaceTest, getSideStripTest) {
@@ -99,7 +99,7 @@
 
 void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
     const auto transform = source.getTransform(dest);
-    EXPECT_EQ(transform.transform(source.content), dest.content)
+    EXPECT_EQ(transform.transform(source.getContent()), dest.getContent())
             << "Source content doesn't map to dest content when projecting " << to_string(source)
             << " onto " << to_string(dest);
 
@@ -113,8 +113,8 @@
     //      |     |                |       *
     //      +-----+                +-------*
     // source(ROTATION_0)      dest (ROTATION_90)
-    const auto sourceStrip = getSideStrip(source.content, source.orientation);
-    const auto destStrip = getSideStrip(dest.content, dest.orientation);
+    const auto sourceStrip = getSideStrip(source.getContent(), source.getOrientation());
+    const auto destStrip = getSideStrip(dest.getContent(), dest.getOrientation());
     ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
     ASSERT_NE(destStrip, Rect::INVALID_RECT);
     const auto mappedStrip = transform.transform(sourceStrip);
@@ -126,16 +126,16 @@
 
 TEST(ProjectionSpaceTest, getTransformWithOrienations) {
     ProjectionSpace source;
-    source.bounds = Rect(12, 13, 678, 789);
-    source.content = Rect(40, 50, 234, 343);
+    source.setBounds(ui::Size(666, 776));
+    source.setContent(Rect(40, 50, 234, 343));
     ProjectionSpace dest;
-    dest.bounds = Rect(17, 18, 879, 564);
-    dest.content = Rect(43, 52, 432, 213);
+    dest.setBounds(ui::Size(862, 546));
+    dest.setContent(Rect(43, 52, 432, 213));
 
     for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
-        source.orientation = ui::Rotation(sourceRot);
+        source.setOrientation(ui::Rotation(sourceRot));
         for (int destRot = 0; destRot <= 3; destRot++) {
-            dest.orientation = ui::Rotation(destRot);
+            dest.setOrientation(ui::Rotation(destRot));
             testTransform(source, dest);
         }
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 5090bb2..7c8e41b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -35,7 +35,7 @@
 
 constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(123u);
 const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
 
 using testing::_;
@@ -201,28 +201,28 @@
  */
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
-    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Mixed))
             .WillOnce(Return(NO_ERROR));
 
     mSurface.prepareFrame(true, true);
 }
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
-    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Gpu))
             .WillOnce(Return(NO_ERROR));
 
     mSurface.prepareFrame(true, false);
 }
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
-    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc))
             .WillOnce(Return(NO_ERROR));
 
     mSurface.prepareFrame(false, true);
 }
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
-    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc))
             .WillOnce(Return(NO_ERROR));
 
     mSurface.prepareFrame(false, false);
diff --git a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
new file mode 100644
index 0000000..c80fde6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <future>
+
+namespace android::compositionengine {
+namespace {
+
+template <class T>
+std::future<T> futureOf(T obj) {
+    std::promise<T> resultPromise;
+    std::future<T> resultFuture = resultPromise.get_future();
+    resultPromise.set_value(std::move(obj));
+    return resultFuture;
+}
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index ec81322..d5a117a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -27,6 +27,8 @@
 #include <utils/Errors.h>
 #include <memory>
 
+#include "tests/TestUtils.h"
+
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 
@@ -53,13 +55,21 @@
 }
 
 MATCHER_P(ClientCompositionTargetSettingsSecureEq, expectedSecureSetting, "") {
-    *result_listener << "ClientCompositionTargetSettings' SecureSettings aren't equal \n";
+    *result_listener << "ClientCompositionTargetSettings' isSecure bits aren't equal \n";
     *result_listener << "expected " << expectedSecureSetting << "\n";
     *result_listener << "actual " << arg.isSecure << "\n";
 
     return expectedSecureSetting == arg.isSecure;
 }
 
+MATCHER_P(ClientCompositionTargetSettingsWhitePointEq, expectedWhitePoint, "") {
+    *result_listener << "ClientCompositionTargetSettings' white points aren't equal \n";
+    *result_listener << "expected " << expectedWhitePoint << "\n";
+    *result_listener << "actual " << arg.whitePointNits << "\n";
+
+    return expectedWhitePoint == arg.whitePointNits;
+}
+
 static const ui::Size kOutputSize = ui::Size(1, 1);
 
 class CachedSetTest : public testing::Test {
@@ -121,7 +131,7 @@
         // set up minimium params needed for rendering
         mOutputState.dataspace = ui::Dataspace::SRGB;
         mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
-        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+        mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
         mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
     }
 }
@@ -342,19 +352,19 @@
     clientCompList2.push_back({});
     clientCompList2[0].alpha = 0.75f;
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
-        EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
-        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
-        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<renderengine::LayerSettings>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+        EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
                   displaySettings.orientation);
-        EXPECT_EQ(0.5f, layers[0]->alpha);
-        EXPECT_EQ(0.75f, layers[1]->alpha);
+        EXPECT_EQ(0.5f, layers[0].alpha);
+        EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
-
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
     EXPECT_CALL(*layerFE1,
@@ -363,7 +373,7 @@
     EXPECT_CALL(*layerFE2,
                 prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
             .WillOnce(Return(clientCompList2));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = false;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
     expectReadyBuffer(cachedSet);
@@ -394,19 +404,20 @@
     clientCompList2.push_back({});
     clientCompList2[0].alpha = 0.75f;
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
-        EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
-        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
-        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<renderengine::LayerSettings>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+        EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
                   displaySettings.orientation);
-        EXPECT_EQ(0.5f, layers[0]->alpha);
-        EXPECT_EQ(0.75f, layers[1]->alpha);
+        EXPECT_EQ(0.5f, layers[0].alpha);
+        EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
     EXPECT_CALL(*layerFE1,
@@ -415,7 +426,55 @@
     EXPECT_CALL(*layerFE2,
                 prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
             .WillOnce(Return(clientCompList2));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    mOutputState.isSecure = true;
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    expectReadyBuffer(cachedSet);
+
+    EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+    EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds());
+
+    // Now check that appending a new cached set properly cleans up RenderEngine resources.
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, renderWhitePoint) {
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.append(CachedSet(layer2));
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+
+    mOutputState.displayBrightnessNits = 400.f;
+
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<renderengine::LayerSettings>&,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+        EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+    };
+
+    EXPECT_CALL(*layerFE1,
+                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                        mOutputState.displayBrightnessNits)))
+            .WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2,
+                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                        mOutputState.displayBrightnessNits)))
+            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
     expectReadyBuffer(cachedSet);
@@ -448,24 +507,25 @@
 
     mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
-        EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
-        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
-        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<renderengine::LayerSettings>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+        EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
                   displaySettings.orientation);
-        EXPECT_EQ(0.5f, layers[0]->alpha);
-        EXPECT_EQ(0.75f, layers[1]->alpha);
+        EXPECT_EQ(0.5f, layers[0].alpha);
+        EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
     EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
     expectReadyBuffer(cachedSet);
 
@@ -650,33 +710,34 @@
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings&,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings&,
+                const std::vector<renderengine::LayerSettings>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 4u);
         {
-            const auto* holePunchSettings = layers[3];
-            EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
-            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
-            EXPECT_TRUE(holePunchSettings->disableBlending);
-            EXPECT_EQ(0.0f, holePunchSettings->alpha);
+            const auto holePunchSettings = layers[3];
+            EXPECT_EQ(nullptr, holePunchSettings.source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings.source.solidColor);
+            EXPECT_TRUE(holePunchSettings.disableBlending);
+            EXPECT_EQ(0.0f, holePunchSettings.alpha);
         }
 
         {
-            const auto* holePunchBackgroundSettings = layers[0];
-            EXPECT_EQ(nullptr, holePunchBackgroundSettings->source.buffer.buffer);
-            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings->source.solidColor);
-            EXPECT_FALSE(holePunchBackgroundSettings->disableBlending);
-            EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
+            const auto holePunchBackgroundSettings = layers[0];
+            EXPECT_EQ(nullptr, holePunchBackgroundSettings.source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings.source.solidColor);
+            EXPECT_FALSE(holePunchBackgroundSettings.disableBlending);
+            EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
         }
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
 }
 
@@ -710,34 +771,35 @@
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings&,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings&,
+                const std::vector<renderengine::LayerSettings>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 4u);
 
         {
-            const auto* holePunchSettings = layers[3];
-            EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
-            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
-            EXPECT_TRUE(holePunchSettings->disableBlending);
-            EXPECT_EQ(0.0f, holePunchSettings->alpha);
+            const auto holePunchSettings = layers[3];
+            EXPECT_EQ(nullptr, holePunchSettings.source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings.source.solidColor);
+            EXPECT_TRUE(holePunchSettings.disableBlending);
+            EXPECT_EQ(0.0f, holePunchSettings.alpha);
         }
 
         {
-            const auto* holePunchBackgroundSettings = layers[0];
-            EXPECT_EQ(nullptr, holePunchBackgroundSettings->source.buffer.buffer);
-            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings->source.solidColor);
-            EXPECT_FALSE(holePunchBackgroundSettings->disableBlending);
-            EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
+            const auto holePunchBackgroundSettings = layers[0];
+            EXPECT_EQ(nullptr, holePunchBackgroundSettings.source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings.source.solidColor);
+            EXPECT_FALSE(holePunchBackgroundSettings.disableBlending);
+            EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
         }
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
 }
 
@@ -859,22 +921,23 @@
                                 BackgroundBlurOnly)))
             .WillOnce(Return(clientCompList3));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings&,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> int32_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings&,
+                const std::vector<renderengine::LayerSettings>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 3u);
-        const auto* blurSettings = layers[2];
-        EXPECT_TRUE(blurSettings->skipContentDraw);
-        EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings->source.solidColor);
-        EXPECT_EQ(0.0f, blurSettings->alpha);
+        const auto blurSettings = layers[2];
+        EXPECT_TRUE(blurSettings.skipContentDraw);
+        EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings.source.solidColor);
+        EXPECT_EQ(0.0f, blurSettings.alpha);
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index a28fb2c..35d051e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -26,6 +26,8 @@
 #include <renderengine/mock/RenderEngine.h>
 #include <chrono>
 
+#include "tests/TestUtils.h"
+
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 using impl::planner::CachedSet;
@@ -141,7 +143,7 @@
         // set up minimium params needed for rendering
         mOutputState.dataspace = ui::Dataspace::SRGB;
         mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
-        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+        mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
     }
 }
 
@@ -167,7 +169,9 @@
 
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -220,6 +224,22 @@
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 }
 
+TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
+    mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
+    mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
+
+    auto& layerState1 = mTestLayers[0]->layerState;
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+    expectAllLayersFlattened(layers);
+}
+
 TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
     auto& layerState1 = mTestLayers[0]->layerState;
     auto& layerState2 = mTestLayers[1]->layerState;
@@ -401,7 +421,9 @@
     // caleed for Layer2 and Layer3
     layerState1->resetFramesSinceBufferUpdate();
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -423,7 +445,9 @@
     layerState1->incrementFramesSinceBufferUpdate();
     mTime += 200ms;
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -474,7 +498,9 @@
     // called for Layer1 and Layer2
     layerState3->resetFramesSinceBufferUpdate();
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -487,11 +513,13 @@
     EXPECT_EQ(nullptr, overrideBuffer5);
 
     // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+    mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
@@ -504,7 +532,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+    mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
@@ -515,8 +543,9 @@
 
     layerState3->incrementFramesSinceBufferUpdate();
     mTime += 200ms;
-
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -531,7 +560,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+    mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
@@ -570,7 +599,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -580,7 +611,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -632,7 +663,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -642,7 +675,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -660,6 +693,145 @@
     EXPECT_EQ(peekThroughLayer1, peekThroughLayer2);
 }
 
+// A test that verifies the hole puch optimization can be done on a single layer.
+TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+
+    // An opaque static background
+    auto& layerState0 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // a rounded updating layer
+    auto& layerState1 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    std::vector<LayerFE::LayerSettings> clientCompositionList = {
+            LayerFE::LayerSettings{},
+    };
+    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+            renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+                                           mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE);
+    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(clientCompositionList));
+
+    const std::vector<const LayerState*> layers = {
+            layerState0.get(),
+            layerState1.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // layer 1 satisfies every condition in CachedSet::requiresHolePunch()
+    mTime += 200ms;
+    layerState1->resetFramesSinceBufferUpdate(); // it is updating
+
+    initializeOverrideBuffer(layers);
+    // Expect no cache invalidation the first time (there's no cache yet)
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
+    // exception that there would be a hole punch above it.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer0);
+
+    // This time we merge the CachedSet in and we should still have only two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    EXPECT_NE(nullptr, overrideBuffer0); // got overridden
+    EXPECT_EQ(nullptr, overrideBuffer1); // did not
+
+    // expect 0's peek though layer to be 1's output layer
+    const auto* peekThroughLayer0 =
+            layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+    const auto* peekThroughLayer1 =
+            layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+    EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0);
+    EXPECT_EQ(nullptr, peekThroughLayer1);
+}
+
+TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f);
+    mTestLayers[0]->layerFECompositionState.buffer = nullptr;
+
+    // An opaque static background
+    auto& layerState0 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // a rounded updating layer
+    auto& layerState1 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    std::vector<LayerFE::LayerSettings> clientCompositionList = {
+            LayerFE::LayerSettings{},
+    };
+    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+            renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+                                           mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE);
+    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(clientCompositionList));
+
+    const std::vector<const LayerState*> layers = {
+            layerState0.get(),
+            layerState1.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // layer 1 satisfies every condition in CachedSet::requiresHolePunch()
+    mTime += 200ms;
+    layerState1->resetFramesSinceBufferUpdate(); // it is updating
+
+    initializeOverrideBuffer(layers);
+    // Expect no cache invalidation the first time (there's no cache yet)
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
+    // exception that there would be a hole punch above it.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer0);
+
+    // This time we merge the CachedSet in and we should still have only two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    EXPECT_NE(nullptr, overrideBuffer0); // got overridden
+    EXPECT_EQ(nullptr, overrideBuffer1); // did not
+
+    // expect 0's peek though layer to be 1's output layer
+    const auto* peekThroughLayer0 =
+            layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+    const auto* peekThroughLayer1 =
+            layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+    EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0);
+    EXPECT_EQ(nullptr, peekThroughLayer1);
+}
+
 TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
     auto& layerState1 = mTestLayers[0]->layerState;
 
@@ -685,7 +857,9 @@
     layerState3->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -729,7 +903,9 @@
     layerState1->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillRepeatedly(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -781,7 +957,9 @@
     layerState1->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -828,7 +1006,9 @@
     layerStateWithBlurBehind->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -869,7 +1049,9 @@
     // Mark the layers inactive
     mTime += 200ms;
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -881,7 +1063,7 @@
 
     // Simulate attempting to render prior to merging the new cached set with the layer stack.
     // Here we should not try to re-render.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We provide the override buffer now that it's rendered
@@ -928,13 +1110,15 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     for (size_t i = 0; i < kMaxDeferRenderAttempts; i++) {
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
         mFlattener->renderCachedSets(mOutputState,
                                      std::chrono::steady_clock::now() -
                                              (kCachedSetRenderDuration + 10ms));
     }
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState,
                                  std::chrono::steady_clock::now() -
                                          (kCachedSetRenderDuration + 10ms));
@@ -968,7 +1152,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -978,7 +1164,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1017,7 +1203,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1027,7 +1215,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1066,7 +1254,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1076,7 +1266,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1087,5 +1277,61 @@
     EXPECT_EQ(nullptr, overrideBuffer3);
 }
 
+TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+    auto& layerState4 = mTestLayers[3]->layerState;
+    const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // Rewrite the first two layers to just be a solid color.
+    mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f);
+    mTestLayers[0]->layerFECompositionState.buffer = nullptr;
+    mTestLayers[1]->layerFECompositionState.color = half4(0.f, 255.f, 0.f, 255.f);
+    mTestLayers[1]->layerFECompositionState.buffer = nullptr;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+            layerState4.get(),
+    };
+
+    initializeFlattener(layers);
+
+    mTime += 200ms;
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+    EXPECT_EQ(nullptr, overrideBuffer4);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+    EXPECT_NE(nullptr, overrideBuffer4);
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 9ad3ab4..84b3fc5 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -27,6 +27,10 @@
 #include "android/hardware_buffer.h"
 #include "compositionengine/LayerFECompositionState.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
 namespace android::compositionengine::impl::planner {
 namespace {
 
@@ -277,33 +281,28 @@
 TEST_F(LayerStateTest, getCompositionType) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.compositionType =
-            hardware::graphics::composer::hal::Composition::DEVICE;
+    layerFECompositionState.compositionType = Composition::DEVICE;
     setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-    EXPECT_EQ(hardware::graphics::composer::hal::Composition::DEVICE,
-              mLayerState->getCompositionType());
+    EXPECT_EQ(Composition::DEVICE, mLayerState->getCompositionType());
 }
 
 TEST_F(LayerStateTest, getCompositionType_forcedClient) {
     OutputLayerCompositionState outputLayerCompositionState;
     outputLayerCompositionState.forceClientComposition = true;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.compositionType =
-            hardware::graphics::composer::hal::Composition::DEVICE;
+    layerFECompositionState.compositionType = Composition::DEVICE;
     setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-    EXPECT_EQ(hardware::graphics::composer::hal::Composition::CLIENT,
-              mLayerState->getCompositionType());
+    EXPECT_EQ(Composition::CLIENT, mLayerState->getCompositionType());
 }
 
 TEST_F(LayerStateTest, updateCompositionType) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.compositionType =
-            hardware::graphics::composer::hal::Composition::DEVICE;
+    layerFECompositionState.compositionType = Composition::DEVICE;
     setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -311,29 +310,25 @@
     mock::OutputLayer newOutputLayer;
     mock::LayerFE newLayerFE;
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.compositionType =
-            hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+    layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
     setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
-    EXPECT_EQ(hardware::graphics::composer::hal::Composition::SOLID_COLOR,
-              mLayerState->getCompositionType());
+    EXPECT_EQ(Composition::SOLID_COLOR, mLayerState->getCompositionType());
     EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType), updates);
 }
 
 TEST_F(LayerStateTest, compareCompositionType) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.compositionType =
-            hardware::graphics::composer::hal::Composition::DEVICE;
+    layerFECompositionState.compositionType = Composition::DEVICE;
     setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
     mock::OutputLayer newOutputLayer;
     mock::LayerFE newLayerFE;
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.compositionType =
-            hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+    layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
     setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 1492707..6038268 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -24,6 +24,10 @@
 #include <gtest/gtest.h>
 #include <log/log.h>
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
 namespace android::compositionengine::impl::planner {
 namespace {
 
@@ -103,7 +107,7 @@
     mock::LayerFE layerFEOne;
     OutputLayerCompositionState outputLayerCompositionStateOne;
     LayerFECompositionState layerFECompositionStateOne;
-    layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+    layerFECompositionStateOne.compositionType = Composition::DEVICE;
     setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
                        layerFECompositionStateOne);
     LayerState layerStateOne(&outputLayerOne);
@@ -112,7 +116,7 @@
     mock::LayerFE layerFETwo;
     OutputLayerCompositionState outputLayerCompositionStateTwo;
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR;
+    layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
     setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
                        layerFECompositionStateTwo);
     LayerState layerStateTwo(&outputLayerTwo);
@@ -370,7 +374,7 @@
 
 TEST_F(PredictionTest, constructPrediction) {
     Plan plan;
-    plan.addLayerType(hal::Composition::DEVICE);
+    plan.addLayerType(Composition::DEVICE);
 
     Prediction prediction({}, plan);
 
@@ -442,13 +446,13 @@
     mock::LayerFE layerFEOne;
     OutputLayerCompositionState outputLayerCompositionStateOne;
     LayerFECompositionState layerFECompositionStateOne;
-    layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+    layerFECompositionStateOne.compositionType = Composition::DEVICE;
     setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
                        layerFECompositionStateOne);
     LayerState layerStateOne(&outputLayerOne);
 
     Plan plan;
-    plan.addLayerType(hal::Composition::DEVICE);
+    plan.addLayerType(Composition::DEVICE);
 
     Predictor predictor;
 
@@ -484,7 +488,7 @@
     LayerState layerStateTwo(&outputLayerTwo);
 
     Plan plan;
-    plan.addLayerType(hal::Composition::DEVICE);
+    plan.addLayerType(Composition::DEVICE);
 
     Predictor predictor;
 
@@ -521,7 +525,7 @@
     LayerState layerStateTwo(&outputLayerTwo);
 
     Plan plan;
-    plan.addLayerType(hal::Composition::DEVICE);
+    plan.addLayerType(Composition::DEVICE);
 
     Predictor predictor;
 
@@ -535,7 +539,7 @@
     EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type);
 
     Plan planTwo;
-    planTwo.addLayerType(hal::Composition::CLIENT);
+    planTwo.addLayerType(Composition::CLIENT);
     predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo);
     // Now trying to retrieve the predicted plan again returns a nullopt instead.
     // TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this.
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 841e79f..3ccc229 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -36,8 +36,7 @@
 
 sp<Layer> ContainerLayer::createClone() {
     sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer(
-            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
-                              LayerMetadata()));
+            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
     layer->setInitialValuesForClone(this);
     return layer;
 }
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4445eea..a36ea72 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -116,11 +116,11 @@
 }
 
 int DisplayDevice::getWidth() const {
-    return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
+    return mCompositionDisplay->getState().displaySpace.getBounds().width;
 }
 
 int DisplayDevice::getHeight() const {
-    return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
+    return mCompositionDisplay->getState().displaySpace.getBounds().height;
 }
 
 void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -139,6 +139,34 @@
     return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
 }
 
+std::pair<gui::DisplayInfo, ui::Transform> DisplayDevice::getInputInfo() const {
+    gui::DisplayInfo info;
+    info.displayId = getLayerStack().id;
+
+    // The physical orientation is set when the orientation of the display panel is
+    // different than the default orientation of the device. Other services like
+    // InputFlinger do not know about this, so we do not need to expose the physical
+    // orientation of the panel outside of SurfaceFlinger.
+    const ui::Rotation inversePhysicalOrientation = ui::ROTATION_0 - mPhysicalOrientation;
+    auto width = getWidth();
+    auto height = getHeight();
+    if (inversePhysicalOrientation == ui::ROTATION_90 ||
+        inversePhysicalOrientation == ui::ROTATION_270) {
+        std::swap(width, height);
+    }
+    const ui::Transform undoPhysicalOrientation(ui::Transform::toRotationFlags(
+                                                        inversePhysicalOrientation),
+                                                width, height);
+    const auto& displayTransform = undoPhysicalOrientation * getTransform();
+    // Send the inverse display transform to input so it can convert display coordinates to
+    // logical display.
+    info.transform = displayTransform.inverse();
+
+    info.logicalWidth = getLayerStackSpaceRect().width();
+    info.logicalHeight = getLayerStackSpaceRect().height();
+    return {info, displayTransform};
+}
+
 // ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
     mPowerMode = mode;
@@ -232,7 +260,7 @@
 }
 
 void DisplayDevice::setLayerStack(ui::LayerStack stack) {
-    mCompositionDisplay->setLayerStackFilter(stack, isInternal());
+    mCompositionDisplay->setLayerFilter({stack, isInternal()});
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->setLayerStack(stack);
     }
@@ -262,13 +290,15 @@
     if (!orientedDisplaySpaceRect.isValid()) {
         // The destination frame can be invalid if it has never been set,
         // in that case we assume the whole display size.
-        orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
+        orientedDisplaySpaceRect =
+                getCompositionDisplay()->getState().displaySpace.getBoundsAsRect();
     }
 
     if (layerStackSpaceRect.isEmpty()) {
         // The layerStackSpaceRect can be invalid if it has never been set, in that case
         // we assume the whole framebuffer size.
-        layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
+        layerStackSpaceRect =
+                getCompositionDisplay()->getState().framebufferSpace.getBoundsAsRect();
         if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
             std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
         }
@@ -281,6 +311,22 @@
                                            orientedDisplaySpaceRect);
 }
 
+void DisplayDevice::stageBrightness(float brightness) {
+    mStagedBrightness = brightness;
+}
+
+void DisplayDevice::persistBrightness(bool needsComposite) {
+    if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) {
+        getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+        mBrightness = *mStagedBrightness;
+    }
+    mStagedBrightness = std::nullopt;
+}
+
+std::optional<float> DisplayDevice::getStagedBrightness() const {
+    return mStagedBrightness;
+}
+
 ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
     return sPrimaryDisplayRotationFlags;
 }
@@ -336,8 +382,8 @@
     return mCompositionDisplay->isSecure();
 }
 
-const Rect& DisplayDevice::getBounds() const {
-    return mCompositionDisplay->getState().displaySpace.bounds;
+const Rect DisplayDevice::getBounds() const {
+    return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
 }
 
 const Region& DisplayDevice::getUndefinedRegion() const {
@@ -349,7 +395,7 @@
 }
 
 ui::LayerStack DisplayDevice::getLayerStack() const {
-    return mCompositionDisplay->getState().layerStackId;
+    return mCompositionDisplay->getState().layerFilter.layerStack;
 }
 
 ui::Transform::RotationFlags DisplayDevice::getTransformHint() const {
@@ -361,11 +407,11 @@
 }
 
 const Rect& DisplayDevice::getLayerStackSpaceRect() const {
-    return mCompositionDisplay->getState().layerStackSpace.content;
+    return mCompositionDisplay->getState().layerStackSpace.getContent();
 }
 
 const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
-    return mCompositionDisplay->getState().orientedDisplaySpace.content;
+    return mCompositionDisplay->getState().orientedDisplaySpace.getContent();
 }
 
 bool DisplayDevice::hasWideColorGamut() const {
@@ -436,9 +482,9 @@
     return false;
 }
 
-void DisplayDevice::onInvalidate() {
+void DisplayDevice::animateRefreshRateOverlay() {
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->onInvalidate();
+        mRefreshRateOverlay->animate();
     }
 }
 
@@ -453,7 +499,7 @@
     std::scoped_lock lock(mActiveModeLock);
     if (mDesiredActiveModeChanged) {
         // If a mode change is pending, just cache the latest request in mDesiredActiveMode
-        const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event;
+        const auto prevConfig = mDesiredActiveMode.event;
         mDesiredActiveMode = info;
         mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
         return false;
@@ -478,7 +524,7 @@
 
 void DisplayDevice::clearDesiredActiveModeState() {
     std::scoped_lock lock(mActiveModeLock);
-    mDesiredActiveMode.event = Scheduler::ModeEvent::None;
+    mDesiredActiveMode.event = scheduler::DisplayModeEvent::None;
     mDesiredActiveModeChanged = false;
 }
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 4d435c7..d2accaa 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -86,9 +86,7 @@
 
     bool isVirtual() const { return !mConnectionType; }
     bool isPrimary() const { return mIsPrimary; }
-    bool isInternal() const {
-        return !isVirtual() && mConnectionType == ui::DisplayConnectionType::Internal;
-    }
+    bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
@@ -101,6 +99,9 @@
     void setLayerStack(ui::LayerStack);
     void setDisplaySize(int width, int height);
     void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
+    void stageBrightness(float brightness) REQUIRES(SF_MAIN_THREAD);
+    void persistBrightness(bool needsComposite) REQUIRES(SF_MAIN_THREAD);
+    bool isBrightnessStale() const REQUIRES(SF_MAIN_THREAD);
     void setFlags(uint32_t flags);
 
     ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
@@ -108,6 +109,7 @@
 
     static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
 
+    std::optional<float> getStagedBrightness() const REQUIRES(SF_MAIN_THREAD);
     ui::Transform::RotationFlags getTransformHint() const;
     const ui::Transform& getTransform() const;
     const Rect& getLayerStackSpaceRect() const;
@@ -158,8 +160,8 @@
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
 
-    const Rect& getBounds() const;
-    const Rect& bounds() const { return getBounds(); }
+    const Rect getBounds() const;
+    const Rect bounds() const { return getBounds(); }
 
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
@@ -169,6 +171,10 @@
         return mDeviceProductInfo;
     }
 
+    // Get the DisplayInfo that will be sent to InputFlinger, and the display transform that should
+    // be applied to all the input windows on the display.
+    std::pair<gui::DisplayInfo, ui::Transform> getInputInfo() const;
+
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
@@ -188,7 +194,7 @@
 
     struct ActiveModeInfo {
         DisplayModePtr mode;
-        scheduler::RefreshRateConfigEvent event = scheduler::RefreshRateConfigEvent::None;
+        scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
 
         bool operator!=(const ActiveModeInfo& other) const {
             return mode != other.mode || event != other.event;
@@ -232,7 +238,7 @@
     void enableRefreshRateOverlay(bool enable, bool showSpinner);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
-    void onInvalidate();
+    void animateRefreshRateOverlay();
 
     void onVsync(nsecs_t timestamp);
     nsecs_t getVsyncPeriodFromHWC() const;
@@ -269,11 +275,13 @@
     hardware::graphics::composer::hal::PowerMode mPowerMode =
             hardware::graphics::composer::hal::PowerMode::OFF;
     DisplayModePtr mActiveMode;
+    std::optional<float> mStagedBrightness = std::nullopt;
+    float mBrightness = -1.f;
     const DisplayModes mSupportedModes;
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
 
-    // TODO(b/74619554): Remove special cases for primary display.
+    // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
 
     uint32_t mFlags = 0;
@@ -311,7 +319,7 @@
     int32_t sequenceId = sNextSequenceId++;
     std::optional<Physical> physical;
     sp<IGraphicBufferProducer> surface;
-    ui::LayerStack layerStack = ui::NO_LAYER_STACK;
+    ui::LayerStack layerStack;
     uint32_t flags = 0;
     Rect layerStackSpaceRect;
     Rect orientedDisplaySpaceRect;
@@ -354,16 +362,4 @@
     DisplayModeId activeModeId;
 };
 
-// Predicates for display lookup.
-
-struct WithLayerStack {
-    explicit WithLayerStack(ui::LayerStack layerStack) : layerStack(layerStack) {}
-
-    bool operator()(const DisplayDevice& display) const {
-        return display.getLayerStack() == layerStack;
-    }
-
-    ui::LayerStack layerStack;
-};
-
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
new file mode 100644
index 0000000..3c578bc
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -0,0 +1,1035 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HwcComposer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "AidlComposerHal.h"
+
+#include <android/binder_ibinder_platform.h>
+#include <android/binder_manager.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <aidl/android/hardware/graphics/composer3/BnComposerCallback.h>
+
+#include <algorithm>
+#include <cinttypes>
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_vec;
+using hardware::Return;
+
+using aidl::android::hardware::graphics::composer3::BnComposerCallback;
+using aidl::android::hardware::graphics::composer3::Capability;
+using aidl::android::hardware::graphics::composer3::PowerMode;
+using aidl::android::hardware::graphics::composer3::VirtualDisplay;
+
+using aidl::android::hardware::graphics::composer3::CommandResultPayload;
+
+using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode;
+using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType;
+using AidlDisplayIdentification =
+        aidl::android::hardware::graphics::composer3::DisplayIdentification;
+using AidlDisplayContentSample = aidl::android::hardware::graphics::composer3::DisplayContentSample;
+using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
+using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
+using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
+using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
+using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
+using AidlPerFrameMetadataBlob = aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob;
+using AidlRenderIntent = aidl::android::hardware::graphics::composer3::RenderIntent;
+using AidlVsyncPeriodChangeConstraints =
+        aidl::android::hardware::graphics::composer3::VsyncPeriodChangeConstraints;
+using AidlVsyncPeriodChangeTimeline =
+        aidl::android::hardware::graphics::composer3::VsyncPeriodChangeTimeline;
+using AidlDisplayContentSamplingAttributes =
+        aidl::android::hardware::graphics::composer3::DisplayContentSamplingAttributes;
+using AidlFormatColorComponent = aidl::android::hardware::graphics::composer3::FormatColorComponent;
+using AidlDisplayConnectionType =
+        aidl::android::hardware::graphics::composer3::DisplayConnectionType;
+
+using AidlColorTransform = aidl::android::hardware::graphics::common::ColorTransform;
+using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+using AidlFRect = aidl::android::hardware::graphics::common::FRect;
+using AidlRect = aidl::android::hardware::graphics::common::Rect;
+using AidlTransform = aidl::android::hardware::graphics::common::Transform;
+
+namespace Hwc2 {
+
+namespace {
+
+template <typename To, typename From>
+To translate(From x) {
+    return static_cast<To>(x);
+}
+
+template <typename To, typename From>
+std::vector<To> translate(const std::vector<From>& in) {
+    std::vector<To> out;
+    out.reserve(in.size());
+    std::transform(in.begin(), in.end(), std::back_inserter(out),
+                   [](From x) { return translate<To>(x); });
+    return out;
+}
+
+template <>
+AidlRect translate(IComposerClient::Rect x) {
+    return AidlRect{
+            .left = x.left,
+            .top = x.top,
+            .right = x.right,
+            .bottom = x.bottom,
+    };
+}
+
+template <>
+AidlFRect translate(IComposerClient::FRect x) {
+    return AidlFRect{
+            .left = x.left,
+            .top = x.top,
+            .right = x.right,
+            .bottom = x.bottom,
+    };
+}
+
+template <>
+AidlPerFrameMetadataBlob translate(IComposerClient::PerFrameMetadataBlob x) {
+    AidlPerFrameMetadataBlob blob;
+    blob.key = translate<AidlPerFrameMetadataKey>(x.key),
+    std::copy(blob.blob.begin(), blob.blob.end(), x.blob.begin());
+    return blob;
+}
+
+template <>
+AidlPerFrameMetadata translate(IComposerClient::PerFrameMetadata x) {
+    return AidlPerFrameMetadata{
+            .key = translate<AidlPerFrameMetadataKey>(x.key),
+            .value = x.value,
+    };
+}
+
+template <>
+DisplayedFrameStats translate(AidlDisplayContentSample x) {
+    return DisplayedFrameStats{
+            .numFrames = static_cast<uint64_t>(x.frameCount),
+            .component_0_sample = translate<uint64_t>(x.sampleComponent0),
+            .component_1_sample = translate<uint64_t>(x.sampleComponent1),
+            .component_2_sample = translate<uint64_t>(x.sampleComponent2),
+            .component_3_sample = translate<uint64_t>(x.sampleComponent3),
+    };
+}
+
+template <>
+AidlVsyncPeriodChangeConstraints translate(IComposerClient::VsyncPeriodChangeConstraints x) {
+    return AidlVsyncPeriodChangeConstraints{
+            .desiredTimeNanos = x.desiredTimeNanos,
+            .seamlessRequired = x.seamlessRequired,
+    };
+}
+
+template <>
+VsyncPeriodChangeTimeline translate(AidlVsyncPeriodChangeTimeline x) {
+    return VsyncPeriodChangeTimeline{
+            .newVsyncAppliedTimeNanos = x.newVsyncAppliedTimeNanos,
+            .refreshRequired = x.refreshRequired,
+            .refreshTimeNanos = x.refreshTimeNanos,
+    };
+}
+
+template <>
+IComposerClient::ClientTargetProperty translate(ClientTargetProperty x) {
+    return IComposerClient::ClientTargetProperty{
+            .pixelFormat = translate<PixelFormat>(x.pixelFormat),
+            .dataspace = translate<Dataspace>(x.dataspace),
+    };
+}
+
+mat4 makeMat4(std::vector<float> in) {
+    return mat4(static_cast<const float*>(in.data()));
+}
+
+} // namespace
+
+class AidlIComposerCallbackWrapper : public BnComposerCallback {
+public:
+    AidlIComposerCallbackWrapper(sp<V2_4::IComposerCallback> callback)
+          : mCallback(std::move(callback)) {}
+
+    ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override {
+        const auto connection = in_connected ? V2_4::IComposerCallback::Connection::CONNECTED
+                                             : V2_4::IComposerCallback::Connection::DISCONNECTED;
+        mCallback->onHotplug(translate<Display>(in_display), connection);
+        return ::ndk::ScopedAStatus::ok();
+    }
+
+    ::ndk::ScopedAStatus onRefresh(int64_t in_display) override {
+        mCallback->onRefresh(translate<Display>(in_display));
+        return ::ndk::ScopedAStatus::ok();
+    }
+    ::ndk::ScopedAStatus onSeamlessPossible(int64_t in_display) override {
+        mCallback->onSeamlessPossible(translate<Display>(in_display));
+        return ::ndk::ScopedAStatus::ok();
+    }
+    ::ndk::ScopedAStatus onVsync(int64_t in_display, int64_t in_timestamp,
+                                 int32_t in_vsyncPeriodNanos) override {
+        mCallback->onVsync_2_4(translate<Display>(in_display), in_timestamp,
+                               static_cast<uint32_t>(in_vsyncPeriodNanos));
+        return ::ndk::ScopedAStatus::ok();
+    }
+    ::ndk::ScopedAStatus onVsyncPeriodTimingChanged(
+            int64_t in_display, const AidlVsyncPeriodChangeTimeline& in_updatedTimeline) override {
+        mCallback->onVsyncPeriodTimingChanged(translate<Display>(in_display),
+                                              translate<V2_4::VsyncPeriodChangeTimeline>(
+                                                      in_updatedTimeline));
+        return ::ndk::ScopedAStatus::ok();
+    }
+
+private:
+    sp<V2_4::IComposerCallback> mCallback;
+};
+
+std::string AidlComposer::instance(const std::string& serviceName) {
+    return std::string(AidlIComposer::descriptor) + "/" + serviceName;
+}
+
+bool AidlComposer::isDeclared(const std::string& serviceName) {
+    return AServiceManager_isDeclared(instance(serviceName).c_str());
+}
+
+AidlComposer::AidlComposer(const std::string& serviceName) {
+    // This only waits if the service is actually declared
+    mAidlComposer = AidlIComposer::fromBinder(
+            ndk::SpAIBinder(AServiceManager_waitForService(instance(serviceName).c_str())));
+    if (!mAidlComposer) {
+        LOG_ALWAYS_FATAL("Failed to get AIDL composer service");
+        return;
+    }
+
+    if (!mAidlComposer->createClient(&mAidlComposerClient).isOk()) {
+        LOG_ALWAYS_FATAL("Can't create AidlComposerClient, fallback to HIDL");
+        return;
+    }
+
+    ALOGI("Loaded AIDL composer3 HAL service");
+}
+
+AidlComposer::~AidlComposer() = default;
+
+bool AidlComposer::isSupported(OptionalFeature feature) const {
+    switch (feature) {
+        case OptionalFeature::RefreshRateSwitching:
+        case OptionalFeature::ExpectedPresentTime:
+        case OptionalFeature::DisplayBrightnessCommand:
+            return true;
+    }
+}
+
+std::vector<IComposer::Capability> AidlComposer::getCapabilities() {
+    std::vector<Capability> capabilities;
+    const auto status = mAidlComposer->getCapabilities(&capabilities);
+    if (!status.isOk()) {
+        ALOGE("getCapabilities failed %s", status.getDescription().c_str());
+        return {};
+    }
+    return translate<IComposer::Capability>(capabilities);
+}
+
+std::string AidlComposer::dumpDebugInfo() {
+    std::string info;
+    const auto status = mAidlComposer->dumpDebugInfo(&info);
+    if (!status.isOk()) {
+        ALOGE("dumpDebugInfo failed %s", status.getDescription().c_str());
+        return {};
+    }
+    return info;
+}
+
+void AidlComposer::registerCallback(const sp<IComposerCallback>& callback) {
+    if (mAidlComposerCallback) {
+        ALOGE("Callback already registered");
+    }
+    mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback);
+    AIBinder_setMinSchedulerPolicy(mAidlComposerCallback->asBinder().get(), SCHED_FIFO, 2);
+
+    const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback);
+    if (!status.isOk()) {
+        ALOGE("registerCallback failed %s", status.getDescription().c_str());
+    }
+}
+
+void AidlComposer::resetCommands() {
+    mWriter.reset();
+}
+
+Error AidlComposer::executeCommands() {
+    return execute();
+}
+
+uint32_t AidlComposer::getMaxVirtualDisplayCount() {
+    int32_t count = 0;
+    const auto status = mAidlComposerClient->getMaxVirtualDisplayCount(&count);
+    if (!status.isOk()) {
+        ALOGE("getMaxVirtualDisplayCount failed %s", status.getDescription().c_str());
+        return 0;
+    }
+    return static_cast<uint32_t>(count);
+}
+
+Error AidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+                                         Display* outDisplay) {
+    using AidlPixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+    const int32_t bufferSlotCount = 1;
+    VirtualDisplay virtualDisplay;
+    const auto status =
+            mAidlComposerClient->createVirtualDisplay(static_cast<int32_t>(width),
+                                                      static_cast<int32_t>(height),
+                                                      static_cast<AidlPixelFormat>(*format),
+                                                      bufferSlotCount, &virtualDisplay);
+
+    if (!status.isOk()) {
+        ALOGE("createVirtualDisplay failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+
+    *outDisplay = translate<Display>(virtualDisplay.display);
+    *format = static_cast<PixelFormat>(virtualDisplay.format);
+    return Error::NONE;
+}
+
+Error AidlComposer::destroyVirtualDisplay(Display display) {
+    const auto status = mAidlComposerClient->destroyVirtualDisplay(translate<int64_t>(display));
+    if (!status.isOk()) {
+        ALOGE("destroyVirtualDisplay failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::acceptDisplayChanges(Display display) {
+    mWriter.acceptDisplayChanges(translate<int64_t>(display));
+    return Error::NONE;
+}
+
+Error AidlComposer::createLayer(Display display, Layer* outLayer) {
+    int64_t layer;
+    const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display),
+                                                         kMaxLayerBufferCount, &layer);
+    if (!status.isOk()) {
+        ALOGE("createLayer failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+
+    *outLayer = translate<Layer>(layer);
+    return Error::NONE;
+}
+
+Error AidlComposer::destroyLayer(Display display, Layer layer) {
+    const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display),
+                                                          translate<int64_t>(layer));
+    if (!status.isOk()) {
+        ALOGE("destroyLayer failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getActiveConfig(Display display, Config* outConfig) {
+    int32_t config;
+    const auto status = mAidlComposerClient->getActiveConfig(translate<int64_t>(display), &config);
+    if (!status.isOk()) {
+        ALOGE("getActiveConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *outConfig = translate<Config>(config);
+    return Error::NONE;
+}
+
+Error AidlComposer::getChangedCompositionTypes(
+        Display display, std::vector<Layer>* outLayers,
+        std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
+    const auto changedLayers = mReader.takeChangedCompositionTypes(translate<int64_t>(display));
+    outLayers->reserve(changedLayers.size());
+    outTypes->reserve(changedLayers.size());
+
+    for (const auto& layer : changedLayers) {
+        outLayers->emplace_back(translate<Layer>(layer.layer));
+        outTypes->emplace_back(layer.composition);
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
+    std::vector<AidlColorMode> modes;
+    const auto status = mAidlComposerClient->getColorModes(translate<int64_t>(display), &modes);
+    if (!status.isOk()) {
+        ALOGE("getColorModes failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *outModes = translate<ColorMode>(modes);
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayAttribute(Display display, Config config,
+                                        IComposerClient::Attribute attribute, int32_t* outValue) {
+    const auto status =
+            mAidlComposerClient->getDisplayAttribute(translate<int64_t>(display),
+                                                     translate<int32_t>(config),
+                                                     static_cast<AidlDisplayAttribute>(attribute),
+                                                     outValue);
+    if (!status.isOk()) {
+        ALOGE("getDisplayAttribute failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
+    std::vector<int32_t> configs;
+    const auto status =
+            mAidlComposerClient->getDisplayConfigs(translate<int64_t>(display), &configs);
+    if (!status.isOk()) {
+        ALOGE("getDisplayConfigs failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *outConfigs = translate<Config>(configs);
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayName(Display display, std::string* outName) {
+    const auto status = mAidlComposerClient->getDisplayName(translate<int64_t>(display), outName);
+    if (!status.isOk()) {
+        ALOGE("getDisplayName failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+                                       std::vector<Layer>* outLayers,
+                                       std::vector<uint32_t>* outLayerRequestMasks) {
+    const auto displayRequests = mReader.takeDisplayRequests(translate<int64_t>(display));
+    *outDisplayRequestMask = translate<uint32_t>(displayRequests.mask);
+    outLayers->reserve(displayRequests.layerRequests.size());
+    outLayerRequestMasks->reserve(displayRequests.layerRequests.size());
+
+    for (const auto& layer : displayRequests.layerRequests) {
+        outLayers->emplace_back(translate<Layer>(layer.layer));
+        outLayerRequestMasks->emplace_back(translate<uint32_t>(layer.mask));
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getDozeSupport(Display display, bool* outSupport) {
+    std::vector<AidlDisplayCapability> capabilities;
+    const auto status =
+            mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities);
+    if (!status.isOk()) {
+        ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *outSupport = std::find(capabilities.begin(), capabilities.end(),
+                            AidlDisplayCapability::DOZE) != capabilities.end();
+    return Error::NONE;
+}
+
+Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+                                       float* outMaxLuminance, float* outMaxAverageLuminance,
+                                       float* outMinLuminance) {
+    AidlHdrCapabilities capabilities;
+    const auto status =
+            mAidlComposerClient->getHdrCapabilities(translate<int64_t>(display), &capabilities);
+    if (!status.isOk()) {
+        ALOGE("getHdrCapabilities failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+
+    *outTypes = translate<Hdr>(capabilities.types);
+    *outMaxLuminance = capabilities.maxLuminance;
+    *outMaxAverageLuminance = capabilities.maxAverageLuminance;
+    *outMinLuminance = capabilities.minLuminance;
+    return Error::NONE;
+}
+
+Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
+                                     std::vector<int>* outReleaseFences) {
+    auto fences = mReader.takeReleaseFences(translate<int64_t>(display));
+    outLayers->reserve(fences.size());
+    outReleaseFences->reserve(fences.size());
+
+    for (auto& fence : fences) {
+        outLayers->emplace_back(translate<Layer>(fence.layer));
+        // take ownership
+        const int fenceOwner = fence.fence.get();
+        *fence.fence.getR() = -1;
+        outReleaseFences->emplace_back(fenceOwner);
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
+    ATRACE_NAME("HwcPresentDisplay");
+    mWriter.presentDisplay(translate<int64_t>(display));
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    auto fence = mReader.takePresentFence(translate<int64_t>(display));
+    // take ownership
+    *outPresentFence = fence.get();
+    *fence.getR() = -1;
+    return Error::NONE;
+}
+
+Error AidlComposer::setActiveConfig(Display display, Config config) {
+    const auto status = mAidlComposerClient->setActiveConfig(translate<int64_t>(display),
+                                                             translate<int32_t>(config));
+    if (!status.isOk()) {
+        ALOGE("setActiveConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+                                    int acquireFence, Dataspace dataspace,
+                                    const std::vector<IComposerClient::Rect>& damage) {
+    const native_handle_t* handle = nullptr;
+    if (target.get()) {
+        handle = target->getNativeBuffer()->handle;
+    }
+
+    mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
+                            translate<aidl::android::hardware::graphics::common::Dataspace>(
+                                    dataspace),
+                            translate<AidlRect>(damage));
+    return Error::NONE;
+}
+
+Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
+    const auto status =
+            mAidlComposerClient->setColorMode(translate<int64_t>(display),
+                                              translate<AidlColorMode>(mode),
+                                              translate<AidlRenderIntent>(renderIntent));
+    if (!status.isOk()) {
+        ALOGE("setColorMode failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::setColorTransform(Display display, const float* matrix) {
+    mWriter.setColorTransform(translate<int64_t>(display), matrix);
+    return Error::NONE;
+}
+
+Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
+                                    int releaseFence) {
+    mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
+    return Error::NONE;
+}
+
+Error AidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+    const auto status = mAidlComposerClient->setPowerMode(translate<int64_t>(display),
+                                                          translate<PowerMode>(mode));
+    if (!status.isOk()) {
+        ALOGE("setPowerMode failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
+    const bool enableVsync = enabled == IComposerClient::Vsync::ENABLE;
+    const auto status =
+            mAidlComposerClient->setVsyncEnabled(translate<int64_t>(display), enableVsync);
+    if (!status.isOk()) {
+        ALOGE("setVsyncEnabled failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::setClientTargetSlotCount(Display display) {
+    const int32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
+    const auto status = mAidlComposerClient->setClientTargetSlotCount(translate<int64_t>(display),
+                                                                      bufferSlotCount);
+    if (!status.isOk()) {
+        ALOGE("setClientTargetSlotCount failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
+                                    uint32_t* outNumTypes, uint32_t* outNumRequests) {
+    ATRACE_NAME("HwcValidateDisplay");
+    mWriter.validateDisplay(translate<int64_t>(display),
+                            ClockMonotonicTimestamp{expectedPresentTime});
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+
+    return Error::NONE;
+}
+
+Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+                                             uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                             int* outPresentFence, uint32_t* state) {
+    ATRACE_NAME("HwcPresentOrValidateDisplay");
+    mWriter.presentOrvalidateDisplay(translate<int64_t>(display),
+                                     ClockMonotonicTimestamp{expectedPresentTime});
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    const auto result = mReader.takePresentOrValidateStage(translate<int64_t>(display));
+    if (!result.has_value()) {
+        *state = translate<uint32_t>(-1);
+        return Error::NO_RESOURCES;
+    }
+
+    *state = translate<uint32_t>(*result);
+
+    if (*result == PresentOrValidate::Result::Presented) {
+        auto fence = mReader.takePresentFence(translate<int64_t>(display));
+        // take ownership
+        *outPresentFence = fence.get();
+        *fence.getR() = -1;
+    }
+
+    if (*result == PresentOrValidate::Result::Validated) {
+        mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+    }
+
+    return Error::NONE;
+}
+
+Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
+    mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
+                                   const sp<GraphicBuffer>& buffer, int acquireFence) {
+    const native_handle_t* handle = nullptr;
+    if (buffer.get()) {
+        handle = buffer->getNativeBuffer()->handle;
+    }
+
+    mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle,
+                           acquireFence);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
+                                          const std::vector<IComposerClient::Rect>& damage) {
+    mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
+                                  translate<AidlRect>(damage));
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerBlendMode(Display display, Layer layer,
+                                      IComposerClient::BlendMode mode) {
+    mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
+                              translate<BlendMode>(mode));
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerColor(Display display, Layer layer, const Color& color) {
+    mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerCompositionType(
+        Display display, Layer layer,
+        aidl::android::hardware::graphics::composer3::Composition type) {
+    mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), type);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
+    mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
+                              translate<AidlDataspace>(dataspace));
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer,
+                                         const IComposerClient::Rect& frame) {
+    mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
+                                 translate<AidlRect>(frame));
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
+    mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerSidebandStream(Display display, Layer layer,
+                                           const native_handle_t* stream) {
+    mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerSourceCrop(Display display, Layer layer,
+                                       const IComposerClient::FRect& crop) {
+    mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
+                               translate<AidlFRect>(crop));
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
+    mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
+                              translate<AidlTransform>(transform));
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer,
+                                          const std::vector<IComposerClient::Rect>& visible) {
+    mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
+                                  translate<AidlRect>(visible));
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
+    mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
+    return Error::NONE;
+}
+
+Error AidlComposer::execute() {
+    const auto& commands = mWriter.getPendingCommands();
+    if (commands.empty()) {
+        mWriter.reset();
+        return Error::NONE;
+    }
+
+    { // scope for results
+        std::vector<CommandResultPayload> results;
+        auto status = mAidlComposerClient->executeCommands(commands, &results);
+        if (!status.isOk()) {
+            ALOGE("executeCommands failed %s", status.getDescription().c_str());
+            return static_cast<Error>(status.getServiceSpecificError());
+        }
+
+        mReader.parse(std::move(results));
+    }
+    const auto commandErrors = mReader.takeErrors();
+    Error error = Error::NONE;
+    for (const auto& cmdErr : commandErrors) {
+        const auto index = static_cast<size_t>(cmdErr.commandIndex);
+        if (index < 0 || index >= commands.size()) {
+            ALOGE("invalid command index %zu", index);
+            return Error::BAD_PARAMETER;
+        }
+
+        const auto& command = commands[index];
+        if (command.validateDisplay || command.presentDisplay || command.presentOrValidateDisplay) {
+            error = translate<Error>(cmdErr.errorCode);
+        } else {
+            ALOGW("command '%s' generated error %" PRId32, command.toString().c_str(),
+                  cmdErr.errorCode);
+        }
+    }
+
+    mWriter.reset();
+
+    return error;
+}
+
+Error AidlComposer::setLayerPerFrameMetadata(
+        Display display, Layer layer,
+        const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
+    mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer),
+                                     translate<AidlPerFrameMetadata>(perFrameMetadatas));
+    return Error::NONE;
+}
+
+std::vector<IComposerClient::PerFrameMetadataKey> AidlComposer::getPerFrameMetadataKeys(
+        Display display) {
+    std::vector<AidlPerFrameMetadataKey> keys;
+    const auto status =
+            mAidlComposerClient->getPerFrameMetadataKeys(translate<int64_t>(display), &keys);
+    if (!status.isOk()) {
+        ALOGE("getPerFrameMetadataKeys failed %s", status.getDescription().c_str());
+        return {};
+    }
+    return translate<IComposerClient::PerFrameMetadataKey>(keys);
+}
+
+Error AidlComposer::getRenderIntents(Display display, ColorMode colorMode,
+                                     std::vector<RenderIntent>* outRenderIntents) {
+    std::vector<AidlRenderIntent> renderIntents;
+    const auto status = mAidlComposerClient->getRenderIntents(translate<int64_t>(display),
+                                                              translate<AidlColorMode>(colorMode),
+                                                              &renderIntents);
+    if (!status.isOk()) {
+        ALOGE("getRenderIntents failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *outRenderIntents = translate<RenderIntent>(renderIntents);
+    return Error::NONE;
+}
+
+Error AidlComposer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) {
+    std::vector<float> matrix;
+    const auto status =
+            mAidlComposerClient->getDataspaceSaturationMatrix(translate<AidlDataspace>(dataspace),
+                                                              &matrix);
+    if (!status.isOk()) {
+        ALOGE("getDataspaceSaturationMatrix failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *outMatrix = makeMat4(matrix);
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                                 std::vector<uint8_t>* outData) {
+    AidlDisplayIdentification displayIdentification;
+    const auto status =
+            mAidlComposerClient->getDisplayIdentificationData(translate<int64_t>(display),
+                                                              &displayIdentification);
+    if (!status.isOk()) {
+        ALOGE("getDisplayIdentificationData failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+
+    *outPort = static_cast<uint8_t>(displayIdentification.port);
+    *outData = displayIdentification.data;
+
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
+    mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix);
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                          Dataspace* outDataspace,
+                                                          uint8_t* outComponentMask) {
+    if (!outFormat || !outDataspace || !outComponentMask) {
+        return Error::BAD_PARAMETER;
+    }
+
+    AidlDisplayContentSamplingAttributes attributes;
+    const auto status =
+            mAidlComposerClient->getDisplayedContentSamplingAttributes(translate<int64_t>(display),
+                                                                       &attributes);
+    if (!status.isOk()) {
+        ALOGE("getDisplayedContentSamplingAttributes failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+
+    *outFormat = translate<PixelFormat>(attributes.format);
+    *outDataspace = translate<Dataspace>(attributes.dataspace);
+    *outComponentMask = static_cast<uint8_t>(attributes.componentMask);
+    return Error::NONE;
+}
+
+Error AidlComposer::setDisplayContentSamplingEnabled(Display display, bool enabled,
+                                                     uint8_t componentMask, uint64_t maxFrames) {
+    const auto status =
+            mAidlComposerClient
+                    ->setDisplayedContentSamplingEnabled(translate<int64_t>(display), enabled,
+                                                         static_cast<AidlFormatColorComponent>(
+                                                                 componentMask),
+                                                         static_cast<int64_t>(maxFrames));
+    if (!status.isOk()) {
+        ALOGE("setDisplayedContentSamplingEnabled failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
+                                              uint64_t timestamp, DisplayedFrameStats* outStats) {
+    if (!outStats) {
+        return Error::BAD_PARAMETER;
+    }
+
+    AidlDisplayContentSample sample;
+    const auto status =
+            mAidlComposerClient->getDisplayedContentSample(translate<int64_t>(display),
+                                                           static_cast<int64_t>(maxFrames),
+                                                           static_cast<int64_t>(timestamp),
+                                                           &sample);
+    if (!status.isOk()) {
+        ALOGE("getDisplayedContentSample failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *outStats = translate<DisplayedFrameStats>(sample);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerPerFrameMetadataBlobs(
+        Display display, Layer layer,
+        const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
+    mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer),
+                                          translate<AidlPerFrameMetadataBlob>(metadata));
+    return Error::NONE;
+}
+
+Error AidlComposer::setDisplayBrightness(Display display, float brightness,
+                                         const DisplayBrightnessOptions& options) {
+    mWriter.setDisplayBrightness(translate<int64_t>(display), brightness);
+
+    if (options.applyImmediately) {
+        return execute();
+    }
+
+    return Error::NONE;
+}
+
+Error AidlComposer::getDisplayCapabilities(Display display,
+                                           std::vector<AidlDisplayCapability>* outCapabilities) {
+    const auto status = mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display),
+                                                                    outCapabilities);
+    if (!status.isOk()) {
+        ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
+        outCapabilities->clear();
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+V2_4::Error AidlComposer::getDisplayConnectionType(
+        Display display, IComposerClient::DisplayConnectionType* outType) {
+    AidlDisplayConnectionType type;
+    const auto status =
+            mAidlComposerClient->getDisplayConnectionType(translate<int64_t>(display), &type);
+    if (!status.isOk()) {
+        ALOGE("getDisplayConnectionType failed %s", status.getDescription().c_str());
+        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+    }
+    *outType = translate<IComposerClient::DisplayConnectionType>(type);
+    return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+    int32_t vsyncPeriod;
+    const auto status =
+            mAidlComposerClient->getDisplayVsyncPeriod(translate<int64_t>(display), &vsyncPeriod);
+    if (!status.isOk()) {
+        ALOGE("getDisplayVsyncPeriod failed %s", status.getDescription().c_str());
+        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+    }
+    *outVsyncPeriod = translate<VsyncPeriodNanos>(vsyncPeriod);
+    return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setActiveConfigWithConstraints(
+        Display display, Config config,
+        const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+        VsyncPeriodChangeTimeline* outTimeline) {
+    AidlVsyncPeriodChangeTimeline timeline;
+    const auto status =
+            mAidlComposerClient
+                    ->setActiveConfigWithConstraints(translate<int64_t>(display),
+                                                     translate<int32_t>(config),
+                                                     translate<AidlVsyncPeriodChangeConstraints>(
+                                                             vsyncPeriodChangeConstraints),
+                                                     &timeline);
+    if (!status.isOk()) {
+        ALOGE("setActiveConfigWithConstraints failed %s", status.getDescription().c_str());
+        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+    }
+    *outTimeline = translate<VsyncPeriodChangeTimeline>(timeline);
+    return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setAutoLowLatencyMode(Display display, bool on) {
+    const auto status = mAidlComposerClient->setAutoLowLatencyMode(translate<int64_t>(display), on);
+    if (!status.isOk()) {
+        ALOGE("setAutoLowLatencyMode failed %s", status.getDescription().c_str());
+        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+    }
+    return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::getSupportedContentTypes(
+        Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+    std::vector<AidlContentType> types;
+    const auto status =
+            mAidlComposerClient->getSupportedContentTypes(translate<int64_t>(displayId), &types);
+    if (!status.isOk()) {
+        ALOGE("getSupportedContentTypes failed %s", status.getDescription().c_str());
+        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+    }
+    *outSupportedContentTypes = translate<IComposerClient::ContentType>(types);
+    return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setContentType(Display display,
+                                         IComposerClient::ContentType contentType) {
+    const auto status =
+            mAidlComposerClient->setContentType(translate<int64_t>(display),
+                                                translate<AidlContentType>(contentType));
+    if (!status.isOk()) {
+        ALOGE("setContentType failed %s", status.getDescription().c_str());
+        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+    }
+    return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
+                                                  const std::vector<uint8_t>&) {
+    // There are no users for this API. See b/209691612.
+    return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error AidlComposer::getLayerGenericMetadataKeys(
+        std::vector<IComposerClient::LayerGenericMetadataKey>*) {
+    // There are no users for this API. See b/209691612.
+    return V2_4::Error::UNSUPPORTED;
+}
+
+Error AidlComposer::getClientTargetProperty(
+        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+        float* whitePointNits) {
+    const auto property = mReader.takeClientTargetProperty(translate<int64_t>(display));
+    *outClientTargetProperty =
+            translate<IComposerClient::ClientTargetProperty>(property.clientTargetProperty);
+    *whitePointNits = property.whitePointNits;
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) {
+    mWriter.setLayerWhitePointNits(translate<int64_t>(display), translate<int64_t>(layer),
+                                   whitePointNits);
+    return Error::NONE;
+}
+
+Error AidlComposer::setLayerBlockingRegion(Display display, Layer layer,
+                                           const std::vector<IComposerClient::Rect>& blocking) {
+    mWriter.setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer),
+                                   translate<AidlRect>(blocking));
+    return Error::NONE;
+}
+} // namespace Hwc2
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
new file mode 100644
index 0000000..cdd16e2
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ComposerHal.h"
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
+#include <android/hardware/graphics/composer3/ComposerClientReader.h>
+#include <android/hardware/graphics/composer3/ComposerClientWriter.h>
+
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+
+namespace android::Hwc2 {
+
+using aidl::android::hardware::graphics::composer3::ComposerClientReader;
+using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
+
+class AidlIComposerCallbackWrapper;
+
+// Composer is a wrapper to IComposer, a proxy to server-side composer.
+class AidlComposer final : public Hwc2::Composer {
+public:
+    static bool isDeclared(const std::string& serviceName);
+
+    explicit AidlComposer(const std::string& serviceName);
+    ~AidlComposer() override;
+
+    bool isSupported(OptionalFeature) const;
+
+    std::vector<IComposer::Capability> getCapabilities() override;
+    std::string dumpDebugInfo() override;
+
+    void registerCallback(const sp<IComposerCallback>& callback) override;
+
+    // Reset all pending commands in the command buffer. Useful if you want to
+    // skip a frame but have already queued some commands.
+    void resetCommands() override;
+
+    // Explicitly flush all pending commands in the command buffer.
+    Error executeCommands() override;
+
+    uint32_t getMaxVirtualDisplayCount() override;
+    Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+                               Display* outDisplay) override;
+    Error destroyVirtualDisplay(Display display) override;
+
+    Error acceptDisplayChanges(Display display) override;
+
+    Error createLayer(Display display, Layer* outLayer) override;
+    Error destroyLayer(Display display, Layer layer) override;
+
+    Error getActiveConfig(Display display, Config* outConfig) override;
+    Error getChangedCompositionTypes(
+            Display display, std::vector<Layer>* outLayers,
+            std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+            override;
+    Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
+    Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+                              int32_t* outValue) override;
+    Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+    Error getDisplayName(Display display, std::string* outName) override;
+
+    Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+                             std::vector<Layer>* outLayers,
+                             std::vector<uint32_t>* outLayerRequestMasks) override;
+
+    Error getDozeSupport(Display display, bool* outSupport) override;
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+                             float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+    Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
+                           std::vector<int>* outReleaseFences) override;
+
+    Error presentDisplay(Display display, int* outPresentFence) override;
+
+    Error setActiveConfig(Display display, Config config) override;
+
+    /*
+     * The composer caches client targets internally.  When target is nullptr,
+     * the composer uses slot to look up the client target from its cache.
+     * When target is not nullptr, the cache is updated with the new target.
+     */
+    Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+                          int acquireFence, Dataspace dataspace,
+                          const std::vector<IComposerClient::Rect>& damage) override;
+    Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
+    Error setColorTransform(Display display, const float* matrix) override;
+    Error setOutputBuffer(Display display, const native_handle_t* buffer,
+                          int releaseFence) override;
+    Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+    Error setClientTargetSlotCount(Display display) override;
+
+    Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+                          uint32_t* outNumRequests) override;
+
+    Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+                                   uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                   int* outPresentFence, uint32_t* state) override;
+
+    Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+    /* see setClientTarget for the purpose of slot */
+    Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
+                         const sp<GraphicBuffer>& buffer, int acquireFence) override;
+    Error setLayerSurfaceDamage(Display display, Layer layer,
+                                const std::vector<IComposerClient::Rect>& damage) override;
+    Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
+    Error setLayerColor(Display display, Layer layer, const Color& color) override;
+    Error setLayerCompositionType(
+            Display display, Layer layer,
+            aidl::android::hardware::graphics::composer3::Composition type) override;
+    Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
+    Error setLayerDisplayFrame(Display display, Layer layer,
+                               const IComposerClient::Rect& frame) override;
+    Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+    Error setLayerSidebandStream(Display display, Layer layer,
+                                 const native_handle_t* stream) override;
+    Error setLayerSourceCrop(Display display, Layer layer,
+                             const IComposerClient::FRect& crop) override;
+    Error setLayerTransform(Display display, Layer layer, Transform transform) override;
+    Error setLayerVisibleRegion(Display display, Layer layer,
+                                const std::vector<IComposerClient::Rect>& visible) override;
+    Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+    // Composer HAL 2.2
+    Error setLayerPerFrameMetadata(
+            Display display, Layer layer,
+            const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
+    std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+            Display display) override;
+    Error getRenderIntents(Display display, ColorMode colorMode,
+                           std::vector<RenderIntent>* outRenderIntents) override;
+    Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
+
+    // Composer HAL 2.3
+    Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                       std::vector<uint8_t>* outData) override;
+    Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+    Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                Dataspace* outDataspace,
+                                                uint8_t* outComponentMask) override;
+    Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
+                                           uint64_t maxFrames) override;
+    Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                    DisplayedFrameStats* outStats) override;
+    Error setLayerPerFrameMetadataBlobs(
+            Display display, Layer layer,
+            const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
+    Error setDisplayBrightness(Display display, float brightness,
+                               const DisplayBrightnessOptions& options) override;
+
+    // Composer HAL 2.4
+    Error getDisplayCapabilities(
+            Display display,
+            std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*
+                    outCapabilities) override;
+    V2_4::Error getDisplayConnectionType(Display display,
+                                         IComposerClient::DisplayConnectionType* outType) override;
+    V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+    V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) override;
+    V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+    V2_4::Error getSupportedContentTypes(
+            Display displayId,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+    V2_4::Error setContentType(Display displayId,
+                               IComposerClient::ContentType contentType) override;
+    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                        bool mandatory, const std::vector<uint8_t>& value) override;
+    V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+    Error getClientTargetProperty(Display display,
+                                  IComposerClient::ClientTargetProperty* outClientTargetProperty,
+                                  float* outClientTargetWhitePointNits) override;
+
+    // AIDL Composer HAL
+    Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
+    Error setLayerBlockingRegion(Display display, Layer layer,
+                                 const std::vector<IComposerClient::Rect>& blocking) override;
+
+private:
+    // Many public functions above simply write a command into the command
+    // queue to batch the calls.  validateDisplay and presentDisplay will call
+    // this function to execute the command queue.
+    Error execute();
+
+    // returns the default instance name for the given service
+    static std::string instance(const std::string& serviceName);
+
+    // 64KiB minus a small space for metadata such as read/write pointers
+    static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
+    // Max number of buffers that may be cached for a given layer
+    // We obtain this number by:
+    // 1. Tightly coupling this cache to the max size of BufferQueue
+    // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
+    static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
+    ComposerClientWriter mWriter;
+    ComposerClientReader mReader;
+
+    // Aidl interface
+    using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer;
+    using AidlIComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
+    std::shared_ptr<AidlIComposer> mAidlComposer;
+    std::shared_ptr<AidlIComposerClient> mAidlComposerClient;
+    std::shared_ptr<AidlIComposerCallbackWrapper> mAidlComposerCallback;
+};
+
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 09734c2..d69a923 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -14,1562 +14,23 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #undef LOG_TAG
 #define LOG_TAG "HwcComposer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "ComposerHal.h"
+#include "AidlComposerHal.h"
+#include "HidlComposerHal.h"
 
-#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
-#include <hidl/HidlTransportSupport.h>
-#include <hidl/HidlTransportUtils.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-#include <algorithm>
-#include <cinttypes>
-
-namespace android {
-
-using hardware::Return;
-using hardware::hidl_vec;
-using hardware::hidl_handle;
-
-namespace Hwc2 {
+namespace android::Hwc2 {
 
 Composer::~Composer() = default;
 
-namespace {
-
-class BufferHandle {
-public:
-    explicit BufferHandle(const native_handle_t* buffer) {
-        // nullptr is not a valid handle to HIDL
-        mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
+std::unique_ptr<Composer> Composer::create(const std::string& serviceName) {
+    if (AidlComposer::isDeclared(serviceName)) {
+        return std::make_unique<AidlComposer>(serviceName);
     }
 
-    operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
-    {
-        return mHandle;
-    }
-
-private:
-    NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 0, 0);
-    hidl_handle mHandle;
-};
-
-class FenceHandle
-{
-public:
-    FenceHandle(int fd, bool owned)
-        : mOwned(owned)
-    {
-        native_handle_t* handle;
-        if (fd >= 0) {
-            handle = native_handle_init(mStorage, 1, 0);
-            handle->data[0] = fd;
-        } else {
-            // nullptr is not a valid handle to HIDL
-            handle = native_handle_init(mStorage, 0, 0);
-        }
-        mHandle = handle;
-    }
-
-    ~FenceHandle()
-    {
-        if (mOwned) {
-            native_handle_close(mHandle);
-        }
-    }
-
-    operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
-    {
-        return mHandle;
-    }
-
-private:
-    bool mOwned;
-    NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 1, 0);
-    hidl_handle mHandle;
-};
-
-// assume NO_RESOURCES when Status::isOk returns false
-constexpr Error kDefaultError = Error::NO_RESOURCES;
-constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
-
-template<typename T, typename U>
-T unwrapRet(Return<T>& ret, const U& default_val)
-{
-    return (ret.isOk()) ? static_cast<T>(ret) :
-        static_cast<T>(default_val);
+    return std::make_unique<HidlComposer>(serviceName);
 }
 
-Error unwrapRet(Return<Error>& ret)
-{
-    return unwrapRet(ret, kDefaultError);
-}
-
-} // anonymous namespace
-
-namespace impl {
-
-Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
-    mComposer = V2_1::IComposer::getService(serviceName);
-
-    if (mComposer == nullptr) {
-        LOG_ALWAYS_FATAL("failed to get hwcomposer service");
-    }
-
-    if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
-        composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
-            if (tmpError == V2_4::Error::NONE) {
-                mClient = tmpClient;
-                mClient_2_2 = tmpClient;
-                mClient_2_3 = tmpClient;
-                mClient_2_4 = tmpClient;
-            }
-        });
-    } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
-        composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
-            if (tmpError == Error::NONE) {
-                mClient = tmpClient;
-                mClient_2_2 = tmpClient;
-                mClient_2_3 = tmpClient;
-            }
-        });
-    } else {
-        mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
-            if (tmpError != Error::NONE) {
-                return;
-            }
-
-            mClient = tmpClient;
-            if (sp<V2_2::IComposer> composer_2_2 = V2_2::IComposer::castFrom(mComposer)) {
-                mClient_2_2 = V2_2::IComposerClient::castFrom(mClient);
-                LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr,
-                                    "IComposer 2.2 did not return IComposerClient 2.2");
-            }
-        });
-    }
-
-    if (mClient == nullptr) {
-        LOG_ALWAYS_FATAL("failed to create composer client");
-    }
-}
-
-Composer::~Composer() = default;
-
-std::vector<IComposer::Capability> Composer::getCapabilities()
-{
-    std::vector<IComposer::Capability> capabilities;
-    mComposer->getCapabilities(
-            [&](const auto& tmpCapabilities) {
-                capabilities = tmpCapabilities;
-            });
-    return capabilities;
-}
-
-std::string Composer::dumpDebugInfo()
-{
-    std::string info;
-    mComposer->dumpDebugInfo([&](const auto& tmpInfo) {
-        info = tmpInfo.c_str();
-    });
-
-    return info;
-}
-
-void Composer::registerCallback(const sp<IComposerCallback>& callback)
-{
-    android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
-    auto ret = [&]() {
-        if (mClient_2_4) {
-            return mClient_2_4->registerCallback_2_4(callback);
-        }
-        return mClient->registerCallback(callback);
-    }();
-    if (!ret.isOk()) {
-        ALOGE("failed to register IComposerCallback");
-    }
-}
-
-void Composer::resetCommands() {
-    mWriter.reset();
-}
-
-Error Composer::executeCommands() {
-    return execute();
-}
-
-uint32_t Composer::getMaxVirtualDisplayCount()
-{
-    auto ret = mClient->getMaxVirtualDisplayCount();
-    return unwrapRet(ret, 0);
-}
-
-Error Composer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                                     Display* outDisplay) {
-    const uint32_t bufferSlotCount = 1;
-    Error error = kDefaultError;
-    if (mClient_2_2) {
-        mClient_2_2->createVirtualDisplay_2_2(width, height,
-                                              static_cast<types::V1_1::PixelFormat>(*format),
-                                              bufferSlotCount,
-                                              [&](const auto& tmpError, const auto& tmpDisplay,
-                                                  const auto& tmpFormat) {
-                                                  error = tmpError;
-                                                  if (error != Error::NONE) {
-                                                      return;
-                                                  }
-
-                                                  *outDisplay = tmpDisplay;
-                                                  *format = static_cast<types::V1_2::PixelFormat>(
-                                                          tmpFormat);
-                                              });
-    } else {
-        mClient->createVirtualDisplay(width, height,
-                static_cast<types::V1_0::PixelFormat>(*format), bufferSlotCount,
-                [&](const auto& tmpError, const auto& tmpDisplay,
-                    const auto& tmpFormat) {
-                    error = tmpError;
-                    if (error != Error::NONE) {
-                        return;
-                    }
-
-                    *outDisplay = tmpDisplay;
-                    *format = static_cast<PixelFormat>(tmpFormat);
-            });
-    }
-
-    return error;
-}
-
-Error Composer::destroyVirtualDisplay(Display display)
-{
-    auto ret = mClient->destroyVirtualDisplay(display);
-    return unwrapRet(ret);
-}
-
-Error Composer::acceptDisplayChanges(Display display)
-{
-    mWriter.selectDisplay(display);
-    mWriter.acceptDisplayChanges();
-    return Error::NONE;
-}
-
-Error Composer::createLayer(Display display, Layer* outLayer)
-{
-    Error error = kDefaultError;
-    mClient->createLayer(display, kMaxLayerBufferCount,
-                         [&](const auto& tmpError, const auto& tmpLayer) {
-                             error = tmpError;
-                             if (error != Error::NONE) {
-                                 return;
-                             }
-
-                             *outLayer = tmpLayer;
-                         });
-
-    return error;
-}
-
-Error Composer::destroyLayer(Display display, Layer layer)
-{
-    auto ret = mClient->destroyLayer(display, layer);
-    return unwrapRet(ret);
-}
-
-Error Composer::getActiveConfig(Display display, Config* outConfig)
-{
-    Error error = kDefaultError;
-    mClient->getActiveConfig(display,
-            [&](const auto& tmpError, const auto& tmpConfig) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
-
-                *outConfig = tmpConfig;
-            });
-
-    return error;
-}
-
-Error Composer::getChangedCompositionTypes(Display display,
-        std::vector<Layer>* outLayers,
-        std::vector<IComposerClient::Composition>* outTypes)
-{
-    mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
-    return Error::NONE;
-}
-
-Error Composer::getColorModes(Display display,
-        std::vector<ColorMode>* outModes)
-{
-    Error error = kDefaultError;
-
-    if (mClient_2_3) {
-        mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
-            if (error != Error::NONE) {
-                return;
-            }
-
-            *outModes = tmpModes;
-        });
-    } else if (mClient_2_2) {
-        mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
-            if (error != Error::NONE) {
-                return;
-            }
-
-            for (types::V1_1::ColorMode colorMode : tmpModes) {
-                outModes->push_back(static_cast<ColorMode>(colorMode));
-            }
-        });
-    } else {
-        mClient->getColorModes(display,
-                [&](const auto& tmpError, const auto& tmpModes) {
-                    error = tmpError;
-                    if (error != Error::NONE) {
-                        return;
-                    }
-                    for (types::V1_0::ColorMode colorMode : tmpModes) {
-                        outModes->push_back(static_cast<ColorMode>(colorMode));
-                    }
-                });
-    }
-
-    return error;
-}
-
-Error Composer::getDisplayAttribute(Display display, Config config,
-        IComposerClient::Attribute attribute, int32_t* outValue)
-{
-    Error error = kDefaultError;
-    if (mClient_2_4) {
-        mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
-                                             [&](const auto& tmpError, const auto& tmpValue) {
-                                                 error = static_cast<Error>(tmpError);
-                                                 if (error != Error::NONE) {
-                                                     return;
-                                                 }
-
-                                                 *outValue = tmpValue;
-                                             });
-    } else {
-        mClient->getDisplayAttribute(display, config,
-                                     static_cast<V2_1::IComposerClient::Attribute>(attribute),
-                                     [&](const auto& tmpError, const auto& tmpValue) {
-                                         error = tmpError;
-                                         if (error != Error::NONE) {
-                                             return;
-                                         }
-
-                                         *outValue = tmpValue;
-                                     });
-    }
-
-    return error;
-}
-
-Error Composer::getDisplayConfigs(Display display,
-        std::vector<Config>* outConfigs)
-{
-    Error error = kDefaultError;
-    mClient->getDisplayConfigs(display,
-            [&](const auto& tmpError, const auto& tmpConfigs) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
-
-                *outConfigs = tmpConfigs;
-            });
-
-    return error;
-}
-
-Error Composer::getDisplayName(Display display, std::string* outName)
-{
-    Error error = kDefaultError;
-    mClient->getDisplayName(display,
-            [&](const auto& tmpError, const auto& tmpName) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
-
-                *outName = tmpName.c_str();
-            });
-
-    return error;
-}
-
-Error Composer::getDisplayRequests(Display display,
-        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
-        std::vector<uint32_t>* outLayerRequestMasks)
-{
-    mReader.takeDisplayRequests(display, outDisplayRequestMask,
-            outLayers, outLayerRequestMasks);
-    return Error::NONE;
-}
-
-Error Composer::getDozeSupport(Display display, bool* outSupport)
-{
-    Error error = kDefaultError;
-    mClient->getDozeSupport(display,
-            [&](const auto& tmpError, const auto& tmpSupport) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
-
-                *outSupport = tmpSupport;
-            });
-
-    return error;
-}
-
-Error Composer::getHdrCapabilities(Display display,
-        std::vector<Hdr>* outTypes, float* outMaxLuminance,
-        float* outMaxAverageLuminance, float* outMinLuminance)
-{
-    Error error = kDefaultError;
-    if (mClient_2_3) {
-        mClient_2_3->getHdrCapabilities_2_3(display,
-                                            [&](const auto& tmpError, const auto& tmpTypes,
-                                                const auto& tmpMaxLuminance,
-                                                const auto& tmpMaxAverageLuminance,
-                                                const auto& tmpMinLuminance) {
-                                                error = tmpError;
-                                                if (error != Error::NONE) {
-                                                    return;
-                                                }
-
-                                                *outTypes = tmpTypes;
-                                                *outMaxLuminance = tmpMaxLuminance;
-                                                *outMaxAverageLuminance = tmpMaxAverageLuminance;
-                                                *outMinLuminance = tmpMinLuminance;
-                                            });
-    } else {
-        mClient->getHdrCapabilities(display,
-                                    [&](const auto& tmpError, const auto& tmpTypes,
-                                        const auto& tmpMaxLuminance,
-                                        const auto& tmpMaxAverageLuminance,
-                                        const auto& tmpMinLuminance) {
-                                        error = tmpError;
-                                        if (error != Error::NONE) {
-                                            return;
-                                        }
-
-                                        outTypes->clear();
-                                        for (auto type : tmpTypes) {
-                                            outTypes->push_back(static_cast<Hdr>(type));
-                                        }
-
-                                        *outMaxLuminance = tmpMaxLuminance;
-                                        *outMaxAverageLuminance = tmpMaxAverageLuminance;
-                                        *outMinLuminance = tmpMinLuminance;
-                                    });
-    }
-
-    return error;
-}
-
-Error Composer::getReleaseFences(Display display,
-        std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
-{
-    mReader.takeReleaseFences(display, outLayers, outReleaseFences);
-    return Error::NONE;
-}
-
-Error Composer::presentDisplay(Display display, int* outPresentFence)
-{
-    ATRACE_NAME("HwcPresentDisplay");
-    mWriter.selectDisplay(display);
-    mWriter.presentDisplay();
-
-    Error error = execute();
-    if (error != Error::NONE) {
-        return error;
-    }
-
-    mReader.takePresentFence(display, outPresentFence);
-
-    return Error::NONE;
-}
-
-Error Composer::setActiveConfig(Display display, Config config)
-{
-    auto ret = mClient->setActiveConfig(display, config);
-    return unwrapRet(ret);
-}
-
-Error Composer::setClientTarget(Display display, uint32_t slot,
-        const sp<GraphicBuffer>& target,
-        int acquireFence, Dataspace dataspace,
-        const std::vector<IComposerClient::Rect>& damage)
-{
-    mWriter.selectDisplay(display);
-
-    const native_handle_t* handle = nullptr;
-    if (target.get()) {
-        handle = target->getNativeBuffer()->handle;
-    }
-
-    mWriter.setClientTarget(slot, handle, acquireFence, dataspace, damage);
-    return Error::NONE;
-}
-
-Error Composer::setColorMode(Display display, ColorMode mode,
-        RenderIntent renderIntent)
-{
-    hardware::Return<Error> ret(kDefaultError);
-    if (mClient_2_3) {
-        ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
-    } else if (mClient_2_2) {
-        ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
-                                            renderIntent);
-    } else {
-        ret = mClient->setColorMode(display,
-                static_cast<types::V1_0::ColorMode>(mode));
-    }
-    return unwrapRet(ret);
-}
-
-Error Composer::setColorTransform(Display display, const float* matrix,
-        ColorTransform hint)
-{
-    mWriter.selectDisplay(display);
-    mWriter.setColorTransform(matrix, hint);
-    return Error::NONE;
-}
-
-Error Composer::setOutputBuffer(Display display, const native_handle_t* buffer,
-        int releaseFence)
-{
-    mWriter.selectDisplay(display);
-    mWriter.setOutputBuffer(0, buffer, dup(releaseFence));
-    return Error::NONE;
-}
-
-Error Composer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
-    Return<Error> ret(Error::UNSUPPORTED);
-    if (mClient_2_2) {
-        ret = mClient_2_2->setPowerMode_2_2(display, mode);
-    } else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
-        ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
-    }
-
-    return unwrapRet(ret);
-}
-
-Error Composer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled)
-{
-    auto ret = mClient->setVsyncEnabled(display, enabled);
-    return unwrapRet(ret);
-}
-
-Error Composer::setClientTargetSlotCount(Display display)
-{
-    const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
-    auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
-    return unwrapRet(ret);
-}
-
-Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
-        uint32_t* outNumRequests)
-{
-    ATRACE_NAME("HwcValidateDisplay");
-    mWriter.selectDisplay(display);
-    mWriter.validateDisplay();
-
-    Error error = execute();
-    if (error != Error::NONE) {
-        return error;
-    }
-
-    mReader.hasChanges(display, outNumTypes, outNumRequests);
-
-    return Error::NONE;
-}
-
-Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
-                               uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
-    ATRACE_NAME("HwcPresentOrValidateDisplay");
-    mWriter.selectDisplay(display);
-    mWriter.presentOrvalidateDisplay();
-
-    Error error = execute();
-    if (error != Error::NONE) {
-        return error;
-    }
-
-   mReader.takePresentOrValidateStage(display, state);
-
-   if (*state == 1) { // Present succeeded
-       mReader.takePresentFence(display, outPresentFence);
-   }
-
-   if (*state == 0) { // Validate succeeded.
-       mReader.hasChanges(display, outNumTypes, outNumRequests);
-   }
-
-   return Error::NONE;
-}
-
-Error Composer::setCursorPosition(Display display, Layer layer,
-        int32_t x, int32_t y)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerCursorPosition(x, y);
-    return Error::NONE;
-}
-
-Error Composer::setLayerBuffer(Display display, Layer layer,
-        uint32_t slot, const sp<GraphicBuffer>& buffer, int acquireFence)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-
-    const native_handle_t* handle = nullptr;
-    if (buffer.get()) {
-        handle = buffer->getNativeBuffer()->handle;
-    }
-
-    mWriter.setLayerBuffer(slot, handle, acquireFence);
-    return Error::NONE;
-}
-
-Error Composer::setLayerSurfaceDamage(Display display, Layer layer,
-        const std::vector<IComposerClient::Rect>& damage)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerSurfaceDamage(damage);
-    return Error::NONE;
-}
-
-Error Composer::setLayerBlendMode(Display display, Layer layer,
-        IComposerClient::BlendMode mode)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerBlendMode(mode);
-    return Error::NONE;
-}
-
-Error Composer::setLayerColor(Display display, Layer layer,
-        const IComposerClient::Color& color)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerColor(color);
-    return Error::NONE;
-}
-
-Error Composer::setLayerCompositionType(Display display, Layer layer,
-        IComposerClient::Composition type)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerCompositionType(type);
-    return Error::NONE;
-}
-
-Error Composer::setLayerDataspace(Display display, Layer layer,
-        Dataspace dataspace)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerDataspace(dataspace);
-    return Error::NONE;
-}
-
-Error Composer::setLayerDisplayFrame(Display display, Layer layer,
-        const IComposerClient::Rect& frame)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerDisplayFrame(frame);
-    return Error::NONE;
-}
-
-Error Composer::setLayerPlaneAlpha(Display display, Layer layer,
-        float alpha)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerPlaneAlpha(alpha);
-    return Error::NONE;
-}
-
-Error Composer::setLayerSidebandStream(Display display, Layer layer,
-        const native_handle_t* stream)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerSidebandStream(stream);
-    return Error::NONE;
-}
-
-Error Composer::setLayerSourceCrop(Display display, Layer layer,
-        const IComposerClient::FRect& crop)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerSourceCrop(crop);
-    return Error::NONE;
-}
-
-Error Composer::setLayerTransform(Display display, Layer layer,
-        Transform transform)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerTransform(transform);
-    return Error::NONE;
-}
-
-Error Composer::setLayerVisibleRegion(Display display, Layer layer,
-        const std::vector<IComposerClient::Rect>& visible)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerVisibleRegion(visible);
-    return Error::NONE;
-}
-
-Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z)
-{
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerZOrder(z);
-    return Error::NONE;
-}
-
-Error Composer::execute()
-{
-    // prepare input command queue
-    bool queueChanged = false;
-    uint32_t commandLength = 0;
-    hidl_vec<hidl_handle> commandHandles;
-    if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
-        mWriter.reset();
-        return Error::NO_RESOURCES;
-    }
-
-    // set up new input command queue if necessary
-    if (queueChanged) {
-        auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
-        auto error = unwrapRet(ret);
-        if (error != Error::NONE) {
-            mWriter.reset();
-            return error;
-        }
-    }
-
-    if (commandLength == 0) {
-        mWriter.reset();
-        return Error::NONE;
-    }
-
-    Error error = kDefaultError;
-    hardware::Return<void> ret;
-    auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
-                             const auto& tmpOutLength, const auto& tmpOutHandles)
-                         {
-                             error = tmpError;
-
-                             // set up new output command queue if necessary
-                             if (error == Error::NONE && tmpOutChanged) {
-                                 error = kDefaultError;
-                                 mClient->getOutputCommandQueue(
-                                     [&](const auto& tmpError,
-                                         const auto& tmpDescriptor)
-                                     {
-                                         error = tmpError;
-                                         if (error != Error::NONE) {
-                                             return;
-                                     }
-
-                                     mReader.setMQDescriptor(tmpDescriptor);
-                                 });
-                             }
-
-                             if (error != Error::NONE) {
-                                 return;
-                             }
-
-                             if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
-                                 error = mReader.parse();
-                                 mReader.reset();
-                             } else {
-                                 error = Error::NO_RESOURCES;
-                             }
-                         };
-    if (mClient_2_2) {
-        ret = mClient_2_2->executeCommands_2_2(commandLength, commandHandles, hidl_callback);
-    } else {
-        ret = mClient->executeCommands(commandLength, commandHandles, hidl_callback);
-    }
-    // executeCommands can fail because of out-of-fd and we do not want to
-    // abort() in that case
-    if (!ret.isOk()) {
-        ALOGE("executeCommands failed because of %s", ret.description().c_str());
-    }
-
-    if (error == Error::NONE) {
-        std::vector<CommandReader::CommandError> commandErrors =
-            mReader.takeErrors();
-
-        for (const auto& cmdErr : commandErrors) {
-            auto command =
-                    static_cast<IComposerClient::Command>(mWriter.getCommand(cmdErr.location));
-
-            if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
-                command == IComposerClient::Command::PRESENT_DISPLAY ||
-                command == IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY) {
-                error = cmdErr.error;
-            } else {
-                ALOGW("command 0x%x generated error %d",
-                        command, cmdErr.error);
-            }
-        }
-    }
-
-    mWriter.reset();
-
-    return error;
-}
-
-// Composer HAL 2.2
-
-Error Composer::setLayerPerFrameMetadata(Display display, Layer layer,
-        const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
-    if (!mClient_2_2) {
-        return Error::UNSUPPORTED;
-    }
-
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerPerFrameMetadata(perFrameMetadatas);
-    return Error::NONE;
-}
-
-std::vector<IComposerClient::PerFrameMetadataKey> Composer::getPerFrameMetadataKeys(
-        Display display) {
-    std::vector<IComposerClient::PerFrameMetadataKey>  keys;
-    if (!mClient_2_2) {
-        return keys;
-    }
-
-    Error error = kDefaultError;
-    if (mClient_2_3) {
-        mClient_2_3->getPerFrameMetadataKeys_2_3(display,
-                                                 [&](const auto& tmpError, const auto& tmpKeys) {
-                                                     error = tmpError;
-                                                     if (error != Error::NONE) {
-                                                         ALOGW("getPerFrameMetadataKeys failed "
-                                                               "with %d",
-                                                               tmpError);
-                                                         return;
-                                                     }
-                                                     keys = tmpKeys;
-                                                 });
-    } else {
-        mClient_2_2
-                ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
-                    error = tmpError;
-                    if (error != Error::NONE) {
-                        ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
-                        return;
-                    }
-
-                    keys.clear();
-                    for (auto key : tmpKeys) {
-                        keys.push_back(static_cast<IComposerClient::PerFrameMetadataKey>(key));
-                    }
-                });
-    }
-
-    return keys;
-}
-
-Error Composer::getRenderIntents(Display display, ColorMode colorMode,
-        std::vector<RenderIntent>* outRenderIntents) {
-    if (!mClient_2_2) {
-        outRenderIntents->push_back(RenderIntent::COLORIMETRIC);
-        return Error::NONE;
-    }
-
-    Error error = kDefaultError;
-
-    auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
-        error = tmpError;
-        if (error != Error::NONE) {
-            return;
-        }
-
-        *outRenderIntents = tmpKeys;
-    };
-
-    if (mClient_2_3) {
-        mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
-    } else {
-        mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
-                                      getRenderIntentsLambda);
-    }
-
-    return error;
-}
-
-Error Composer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix)
-{
-    if (!mClient_2_2) {
-        *outMatrix = mat4();
-        return Error::NONE;
-    }
-
-    Error error = kDefaultError;
-    mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
-                                              [&](const auto& tmpError, const auto& tmpMatrix) {
-                                                  error = tmpError;
-                                                  if (error != Error::NONE) {
-                                                      return;
-                                                  }
-                                                  *outMatrix = mat4(tmpMatrix.data());
-                                              });
-
-    return error;
-}
-
-// Composer HAL 2.3
-
-Error Composer::getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                             std::vector<uint8_t>* outData) {
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-
-    Error error = kDefaultError;
-    mClient_2_3->getDisplayIdentificationData(display,
-                                              [&](const auto& tmpError, const auto& tmpPort,
-                                                  const auto& tmpData) {
-                                                  error = tmpError;
-                                                  if (error != Error::NONE) {
-                                                      return;
-                                                  }
-
-                                                  *outPort = tmpPort;
-                                                  *outData = tmpData;
-                                              });
-
-    return error;
-}
-
-Error Composer::setLayerColorTransform(Display display, Layer layer, const float* matrix)
-{
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerColorTransform(matrix);
-    return Error::NONE;
-}
-
-Error Composer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
-                                                      Dataspace* outDataspace,
-                                                      uint8_t* outComponentMask) {
-    if (!outFormat || !outDataspace || !outComponentMask) {
-        return Error::BAD_PARAMETER;
-    }
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-    Error error = kDefaultError;
-    mClient_2_3->getDisplayedContentSamplingAttributes(display,
-                                                       [&](const auto tmpError,
-                                                           const auto& tmpFormat,
-                                                           const auto& tmpDataspace,
-                                                           const auto& tmpComponentMask) {
-                                                           error = tmpError;
-                                                           if (error == Error::NONE) {
-                                                               *outFormat = tmpFormat;
-                                                               *outDataspace = tmpDataspace;
-                                                               *outComponentMask =
-                                                                       static_cast<uint8_t>(
-                                                                               tmpComponentMask);
-                                                           }
-                                                       });
-    return error;
-}
-
-Error Composer::setDisplayContentSamplingEnabled(Display display, bool enabled,
-                                                 uint8_t componentMask, uint64_t maxFrames) {
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-
-    auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
-                          : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
-    return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
-                                                           maxFrames);
-}
-
-Error Composer::getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
-                                          DisplayedFrameStats* outStats) {
-    if (!outStats) {
-        return Error::BAD_PARAMETER;
-    }
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-    Error error = kDefaultError;
-    mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
-                                           [&](const auto tmpError, auto tmpNumFrames,
-                                               const auto& tmpSamples0, const auto& tmpSamples1,
-                                               const auto& tmpSamples2, const auto& tmpSamples3) {
-                                               error = tmpError;
-                                               if (error == Error::NONE) {
-                                                   outStats->numFrames = tmpNumFrames;
-                                                   outStats->component_0_sample = tmpSamples0;
-                                                   outStats->component_1_sample = tmpSamples1;
-                                                   outStats->component_2_sample = tmpSamples2;
-                                                   outStats->component_3_sample = tmpSamples3;
-                                               }
-                                           });
-    return error;
-}
-
-Error Composer::setLayerPerFrameMetadataBlobs(
-        Display display, Layer layer,
-        const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerPerFrameMetadataBlobs(metadata);
-    return Error::NONE;
-}
-
-Error Composer::setDisplayBrightness(Display display, float brightness) {
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-    return mClient_2_3->setDisplayBrightness(display, brightness);
-}
-
-// Composer HAL 2.4
-
-Error Composer::getDisplayCapabilities(Display display,
-                                       std::vector<DisplayCapability>* outCapabilities) {
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-
-    V2_4::Error error = kDefaultError_2_4;
-    if (mClient_2_4) {
-        mClient_2_4->getDisplayCapabilities_2_4(display,
-                                                [&](const auto& tmpError, const auto& tmpCaps) {
-                                                    error = tmpError;
-                                                    if (error != V2_4::Error::NONE) {
-                                                        return;
-                                                    }
-                                                    *outCapabilities = tmpCaps;
-                                                });
-    } else {
-        mClient_2_3
-                ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
-                    error = static_cast<V2_4::Error>(tmpError);
-                    if (error != V2_4::Error::NONE) {
-                        return;
-                    }
-
-                    outCapabilities->resize(tmpCaps.size());
-                    std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
-                                   [](auto cap) { return static_cast<DisplayCapability>(cap); });
-                });
-    }
-
-    return static_cast<Error>(error);
-}
-
-V2_4::Error Composer::getDisplayConnectionType(Display display,
-                                               IComposerClient::DisplayConnectionType* outType) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-
-    Error error = kDefaultError_2_4;
-    mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
-        error = tmpError;
-        if (error != V2_4::Error::NONE) {
-            return;
-        }
-
-        *outType = tmpType;
-    });
-
-    return error;
-}
-
-V2_4::Error Composer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-
-    Error error = kDefaultError_2_4;
-    mClient_2_4->getDisplayVsyncPeriod(display,
-                                       [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
-                                           error = tmpError;
-                                           if (error != Error::NONE) {
-                                               return;
-                                           }
-
-                                           *outVsyncPeriod = tmpVsyncPeriod;
-                                       });
-
-    return error;
-}
-
-V2_4::Error Composer::setActiveConfigWithConstraints(
-        Display display, Config config,
-        const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
-        VsyncPeriodChangeTimeline* outTimeline) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-
-    Error error = kDefaultError_2_4;
-    mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
-                                                [&](const auto& tmpError, const auto& tmpTimeline) {
-                                                    error = tmpError;
-                                                    if (error != Error::NONE) {
-                                                        return;
-                                                    }
-
-                                                    *outTimeline = tmpTimeline;
-                                                });
-
-    return error;
-}
-
-V2_4::Error Composer::setAutoLowLatencyMode(Display display, bool on) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-
-    return mClient_2_4->setAutoLowLatencyMode(display, on);
-}
-
-V2_4::Error Composer::getSupportedContentTypes(
-        Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-
-    Error error = kDefaultError_2_4;
-    mClient_2_4->getSupportedContentTypes(displayId,
-                                          [&](const auto& tmpError,
-                                              const auto& tmpSupportedContentTypes) {
-                                              error = tmpError;
-                                              if (error != Error::NONE) {
-                                                  return;
-                                              }
-
-                                              *outSupportedContentTypes = tmpSupportedContentTypes;
-                                          });
-    return error;
-}
-
-V2_4::Error Composer::setContentType(Display display, IComposerClient::ContentType contentType) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-
-    return mClient_2_4->setContentType(display, contentType);
-}
-
-V2_4::Error Composer::setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
-                                              bool mandatory, const std::vector<uint8_t>& value) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-    mWriter.selectDisplay(display);
-    mWriter.selectLayer(layer);
-    mWriter.setLayerGenericMetadata(key, mandatory, value);
-    return Error::NONE;
-}
-
-V2_4::Error Composer::getLayerGenericMetadataKeys(
-        std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
-    using Error = V2_4::Error;
-    if (!mClient_2_4) {
-        return Error::UNSUPPORTED;
-    }
-    Error error = kDefaultError_2_4;
-    mClient_2_4->getLayerGenericMetadataKeys([&](const auto& tmpError, const auto& tmpKeys) {
-        error = tmpError;
-        if (error != Error::NONE) {
-            return;
-        }
-
-        *outKeys = tmpKeys;
-    });
-    return error;
-}
-
-Error Composer::getClientTargetProperty(
-        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
-    mReader.takeClientTargetProperty(display, outClientTargetProperty);
-    return Error::NONE;
-}
-
-CommandReader::~CommandReader()
-{
-    resetData();
-}
-
-Error CommandReader::parse()
-{
-    resetData();
-
-    IComposerClient::Command command;
-    uint16_t length = 0;
-
-    while (!isEmpty()) {
-        if (!beginCommand(&command, &length)) {
-            break;
-        }
-
-        bool parsed = false;
-        switch (command) {
-        case IComposerClient::Command::SELECT_DISPLAY:
-            parsed = parseSelectDisplay(length);
-            break;
-        case IComposerClient::Command::SET_ERROR:
-            parsed = parseSetError(length);
-            break;
-        case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
-            parsed = parseSetChangedCompositionTypes(length);
-            break;
-        case IComposerClient::Command::SET_DISPLAY_REQUESTS:
-            parsed = parseSetDisplayRequests(length);
-            break;
-        case IComposerClient::Command::SET_PRESENT_FENCE:
-            parsed = parseSetPresentFence(length);
-            break;
-        case IComposerClient::Command::SET_RELEASE_FENCES:
-            parsed = parseSetReleaseFences(length);
-            break;
-        case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
-            parsed = parseSetPresentOrValidateDisplayResult(length);
-            break;
-        case IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY:
-            parsed = parseSetClientTargetProperty(length);
-            break;
-        default:
-            parsed = false;
-            break;
-        }
-
-        endCommand();
-
-        if (!parsed) {
-            ALOGE("failed to parse command 0x%x length %" PRIu16,
-                    command, length);
-            break;
-        }
-    }
-
-    return isEmpty() ? Error::NONE : Error::NO_RESOURCES;
-}
-
-bool CommandReader::parseSelectDisplay(uint16_t length)
-{
-    if (length != CommandWriterBase::kSelectDisplayLength) {
-        return false;
-    }
-
-    mCurrentReturnData = &mReturnData[read64()];
-
-    return true;
-}
-
-bool CommandReader::parseSetError(uint16_t length)
-{
-    if (length != CommandWriterBase::kSetErrorLength) {
-        return false;
-    }
-
-    auto location = read();
-    auto error = static_cast<Error>(readSigned());
-
-    mErrors.emplace_back(CommandError{location, error});
-
-    return true;
-}
-
-bool CommandReader::parseSetChangedCompositionTypes(uint16_t length)
-{
-    // (layer id, composition type) pairs
-    if (length % 3 != 0 || !mCurrentReturnData) {
-        return false;
-    }
-
-    uint32_t count = length / 3;
-    mCurrentReturnData->changedLayers.reserve(count);
-    mCurrentReturnData->compositionTypes.reserve(count);
-    while (count > 0) {
-        auto layer = read64();
-        auto type = static_cast<IComposerClient::Composition>(readSigned());
-
-        mCurrentReturnData->changedLayers.push_back(layer);
-        mCurrentReturnData->compositionTypes.push_back(type);
-
-        count--;
-    }
-
-    return true;
-}
-
-bool CommandReader::parseSetDisplayRequests(uint16_t length)
-{
-    // display requests followed by (layer id, layer requests) pairs
-    if (length % 3 != 1 || !mCurrentReturnData) {
-        return false;
-    }
-
-    mCurrentReturnData->displayRequests = read();
-
-    uint32_t count = (length - 1) / 3;
-    mCurrentReturnData->requestedLayers.reserve(count);
-    mCurrentReturnData->requestMasks.reserve(count);
-    while (count > 0) {
-        auto layer = read64();
-        auto layerRequestMask = read();
-
-        mCurrentReturnData->requestedLayers.push_back(layer);
-        mCurrentReturnData->requestMasks.push_back(layerRequestMask);
-
-        count--;
-    }
-
-    return true;
-}
-
-bool CommandReader::parseSetPresentFence(uint16_t length)
-{
-    if (length != CommandWriterBase::kSetPresentFenceLength ||
-            !mCurrentReturnData) {
-        return false;
-    }
-
-    if (mCurrentReturnData->presentFence >= 0) {
-        close(mCurrentReturnData->presentFence);
-    }
-    mCurrentReturnData->presentFence = readFence();
-
-    return true;
-}
-
-bool CommandReader::parseSetReleaseFences(uint16_t length)
-{
-    // (layer id, release fence index) pairs
-    if (length % 3 != 0 || !mCurrentReturnData) {
-        return false;
-    }
-
-    uint32_t count = length / 3;
-    mCurrentReturnData->releasedLayers.reserve(count);
-    mCurrentReturnData->releaseFences.reserve(count);
-    while (count > 0) {
-        auto layer = read64();
-        auto fence = readFence();
-
-        mCurrentReturnData->releasedLayers.push_back(layer);
-        mCurrentReturnData->releaseFences.push_back(fence);
-
-        count--;
-    }
-
-    return true;
-}
-
-bool CommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length)
-{
-    if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
-        return false;
-    }
-    mCurrentReturnData->presentOrValidateState = read();
-    return true;
-}
-
-bool CommandReader::parseSetClientTargetProperty(uint16_t length) {
-    if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) {
-        return false;
-    }
-    mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned());
-    mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned());
-    return true;
-}
-
-void CommandReader::resetData()
-{
-    mErrors.clear();
-
-    for (auto& data : mReturnData) {
-        if (data.second.presentFence >= 0) {
-            close(data.second.presentFence);
-        }
-        for (auto fence : data.second.releaseFences) {
-            if (fence >= 0) {
-                close(fence);
-            }
-        }
-    }
-
-    mReturnData.clear();
-    mCurrentReturnData = nullptr;
-}
-
-std::vector<CommandReader::CommandError> CommandReader::takeErrors()
-{
-    return std::move(mErrors);
-}
-
-bool CommandReader::hasChanges(Display display,
-        uint32_t* outNumChangedCompositionTypes,
-        uint32_t* outNumLayerRequestMasks) const
-{
-    auto found = mReturnData.find(display);
-    if (found == mReturnData.end()) {
-        *outNumChangedCompositionTypes = 0;
-        *outNumLayerRequestMasks = 0;
-        return false;
-    }
-
-    const ReturnData& data = found->second;
-
-    *outNumChangedCompositionTypes = data.compositionTypes.size();
-    *outNumLayerRequestMasks = data.requestMasks.size();
-
-    return !(data.compositionTypes.empty() && data.requestMasks.empty());
-}
-
-void CommandReader::takeChangedCompositionTypes(Display display,
-        std::vector<Layer>* outLayers,
-        std::vector<IComposerClient::Composition>* outTypes)
-{
-    auto found = mReturnData.find(display);
-    if (found == mReturnData.end()) {
-        outLayers->clear();
-        outTypes->clear();
-        return;
-    }
-
-    ReturnData& data = found->second;
-
-    *outLayers = std::move(data.changedLayers);
-    *outTypes = std::move(data.compositionTypes);
-}
-
-void CommandReader::takeDisplayRequests(Display display,
-        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
-        std::vector<uint32_t>* outLayerRequestMasks)
-{
-    auto found = mReturnData.find(display);
-    if (found == mReturnData.end()) {
-        *outDisplayRequestMask = 0;
-        outLayers->clear();
-        outLayerRequestMasks->clear();
-        return;
-    }
-
-    ReturnData& data = found->second;
-
-    *outDisplayRequestMask = data.displayRequests;
-    *outLayers = std::move(data.requestedLayers);
-    *outLayerRequestMasks = std::move(data.requestMasks);
-}
-
-void CommandReader::takeReleaseFences(Display display,
-        std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
-{
-    auto found = mReturnData.find(display);
-    if (found == mReturnData.end()) {
-        outLayers->clear();
-        outReleaseFences->clear();
-        return;
-    }
-
-    ReturnData& data = found->second;
-
-    *outLayers = std::move(data.releasedLayers);
-    *outReleaseFences = std::move(data.releaseFences);
-}
-
-void CommandReader::takePresentFence(Display display, int* outPresentFence)
-{
-    auto found = mReturnData.find(display);
-    if (found == mReturnData.end()) {
-        *outPresentFence = -1;
-        return;
-    }
-
-    ReturnData& data = found->second;
-
-    *outPresentFence = data.presentFence;
-    data.presentFence = -1;
-}
-
-void CommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
-    auto found = mReturnData.find(display);
-    if (found == mReturnData.end()) {
-        *state= -1;
-        return;
-    }
-    ReturnData& data = found->second;
-    *state = data.presentOrValidateState;
-}
-
-void CommandReader::takeClientTargetProperty(
-        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
-    auto found = mReturnData.find(display);
-
-    // If not found, return the default values.
-    if (found == mReturnData.end()) {
-        outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
-        outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
-        return;
-    }
-
-    ReturnData& data = found->second;
-    *outClientTargetProperty = data.clientTargetProperty;
-}
-
-} // namespace impl
-} // namespace Hwc2
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index fe114b9..bb4b784 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -14,24 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_COMPOSER_HAL_H
-#define ANDROID_SF_COMPOSER_HAL_H
+#pragma once
 
 #include <memory>
-#include <optional>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
 
-#include <android/hardware/graphics/common/1.1/types.h>
-#include <android/hardware/graphics/composer/2.4/IComposer.h>
-#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 #include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
 #include <gui/BufferQueue.h>
 #include <gui/HdrMetadata.h>
@@ -40,12 +31,14 @@
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 
+#include <aidl/android/hardware/graphics/composer3/Color.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
-namespace android {
-
-namespace Hwc2 {
+namespace android::Hwc2 {
 
 namespace types = hardware::graphics::common;
 
@@ -73,15 +66,25 @@
 using V2_4::IComposerClient;
 using V2_4::VsyncPeriodChangeTimeline;
 using V2_4::VsyncPeriodNanos;
-using DisplayCapability = IComposerClient::DisplayCapability;
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
 
 class Composer {
 public:
+    static std::unique_ptr<Composer> create(const std::string& serviceName);
+
     virtual ~Composer() = 0;
 
+    enum class OptionalFeature {
+        RefreshRateSwitching,
+        ExpectedPresentTime,
+        // Whether setDisplayBrightness is able to be applied as part of a display command.
+        DisplayBrightnessCommand,
+    };
+
+    virtual bool isSupported(OptionalFeature) const = 0;
+
     virtual std::vector<IComposer::Capability> getCapabilities() = 0;
     virtual std::string dumpDebugInfo() = 0;
 
@@ -107,7 +110,7 @@
     virtual Error getActiveConfig(Display display, Config* outConfig) = 0;
     virtual Error getChangedCompositionTypes(
             Display display, std::vector<Layer>* outLayers,
-            std::vector<IComposerClient::Composition>* outTypes) = 0;
+            std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) = 0;
     virtual Error getColorModes(Display display, std::vector<ColorMode>* outModes) = 0;
     virtual Error getDisplayAttribute(Display display, Config config,
                                       IComposerClient::Attribute attribute, int32_t* outValue) = 0;
@@ -139,7 +142,7 @@
                                   int acquireFence, Dataspace dataspace,
                                   const std::vector<IComposerClient::Rect>& damage) = 0;
     virtual Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) = 0;
-    virtual Error setColorTransform(Display display, const float* matrix, ColorTransform hint) = 0;
+    virtual Error setColorTransform(Display display, const float* matrix) = 0;
     virtual Error setOutputBuffer(Display display, const native_handle_t* buffer,
                                   int releaseFence) = 0;
     virtual Error setPowerMode(Display display, IComposerClient::PowerMode mode) = 0;
@@ -147,12 +150,12 @@
 
     virtual Error setClientTargetSlotCount(Display display) = 0;
 
-    virtual Error validateDisplay(Display display, uint32_t* outNumTypes,
-                                  uint32_t* outNumRequests) = 0;
+    virtual Error validateDisplay(Display display, nsecs_t expectedPresentTime,
+                                  uint32_t* outNumTypes, uint32_t* outNumRequests) = 0;
 
-    virtual Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
-                                           uint32_t* outNumRequests, int* outPresentFence,
-                                           uint32_t* state) = 0;
+    virtual Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+                                           uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                           int* outPresentFence, uint32_t* state) = 0;
 
     virtual Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) = 0;
     /* see setClientTarget for the purpose of slot */
@@ -162,10 +165,12 @@
                                         const std::vector<IComposerClient::Rect>& damage) = 0;
     virtual Error setLayerBlendMode(Display display, Layer layer,
                                     IComposerClient::BlendMode mode) = 0;
-    virtual Error setLayerColor(Display display, Layer layer,
-                                const IComposerClient::Color& color) = 0;
-    virtual Error setLayerCompositionType(Display display, Layer layer,
-                                          IComposerClient::Composition type) = 0;
+    virtual Error setLayerColor(
+            Display display, Layer layer,
+            const aidl::android::hardware::graphics::composer3::Color& color) = 0;
+    virtual Error setLayerCompositionType(
+            Display display, Layer layer,
+            aidl::android::hardware::graphics::composer3::Composition type) = 0;
     virtual Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) = 0;
     virtual Error setLayerDisplayFrame(Display display, Layer layer,
                                        const IComposerClient::Rect& frame) = 0;
@@ -203,12 +208,26 @@
                                             DisplayedFrameStats* outStats) = 0;
     virtual Error setLayerPerFrameMetadataBlobs(
             Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0;
-    virtual Error setDisplayBrightness(Display display, float brightness) = 0;
+    // Options for setting the display brightness
+    struct DisplayBrightnessOptions {
+        // If true, then immediately submits a brightness change request to composer. Otherwise,
+        // submission of the brightness change may be deferred until presenting the next frame.
+        // applyImmediately should only be false if OptionalFeature::DisplayBrightnessCommand is
+        // supported.
+        bool applyImmediately = true;
+
+        bool operator==(const DisplayBrightnessOptions& other) const {
+            return applyImmediately == other.applyImmediately;
+        }
+    };
+    virtual Error setDisplayBrightness(Display display, float brightness,
+                                       const DisplayBrightnessOptions& options) = 0;
 
     // Composer HAL 2.4
-    virtual bool isVsyncPeriodSwitchSupported() = 0;
-    virtual Error getDisplayCapabilities(Display display,
-                                         std::vector<DisplayCapability>* outCapabilities) = 0;
+    virtual Error getDisplayCapabilities(
+            Display display,
+            std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*
+                    outCapabilities) = 0;
     virtual V2_4::Error getDisplayConnectionType(
             Display display, IComposerClient::DisplayConnectionType* outType) = 0;
     virtual V2_4::Error getDisplayVsyncPeriod(Display display,
@@ -230,282 +249,13 @@
     virtual V2_4::Error getLayerGenericMetadataKeys(
             std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
     virtual Error getClientTargetProperty(
-            Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
+            Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+            float* outWhitePointNits) = 0;
+
+    // AIDL Composer
+    virtual Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) = 0;
+    virtual Error setLayerBlockingRegion(Display display, Layer layer,
+                                         const std::vector<IComposerClient::Rect>& blocking) = 0;
 };
 
-namespace impl {
-
-class CommandReader : public CommandReaderBase {
-public:
-    ~CommandReader();
-
-    // Parse and execute commands from the command queue.  The commands are
-    // actually return values from the server and will be saved in ReturnData.
-    Error parse();
-
-    // Get and clear saved errors.
-    struct CommandError {
-        uint32_t location;
-        Error error;
-    };
-    std::vector<CommandError> takeErrors();
-
-    bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
-            uint32_t* outNumLayerRequestMasks) const;
-
-    // Get and clear saved changed composition types.
-    void takeChangedCompositionTypes(Display display,
-            std::vector<Layer>* outLayers,
-            std::vector<IComposerClient::Composition>* outTypes);
-
-    // Get and clear saved display requests.
-    void takeDisplayRequests(Display display,
-        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
-        std::vector<uint32_t>* outLayerRequestMasks);
-
-    // Get and clear saved release fences.
-    void takeReleaseFences(Display display, std::vector<Layer>* outLayers,
-            std::vector<int>* outReleaseFences);
-
-    // Get and clear saved present fence.
-    void takePresentFence(Display display, int* outPresentFence);
-
-    // Get what stage succeeded during PresentOrValidate: Present or Validate
-    void takePresentOrValidateStage(Display display, uint32_t * state);
-
-    // Get the client target properties requested by hardware composer.
-    void takeClientTargetProperty(Display display,
-                                  IComposerClient::ClientTargetProperty* outClientTargetProperty);
-
-private:
-    void resetData();
-
-    bool parseSelectDisplay(uint16_t length);
-    bool parseSetError(uint16_t length);
-    bool parseSetChangedCompositionTypes(uint16_t length);
-    bool parseSetDisplayRequests(uint16_t length);
-    bool parseSetPresentFence(uint16_t length);
-    bool parseSetReleaseFences(uint16_t length);
-    bool parseSetPresentOrValidateDisplayResult(uint16_t length);
-    bool parseSetClientTargetProperty(uint16_t length);
-
-    struct ReturnData {
-        uint32_t displayRequests = 0;
-
-        std::vector<Layer> changedLayers;
-        std::vector<IComposerClient::Composition> compositionTypes;
-
-        std::vector<Layer> requestedLayers;
-        std::vector<uint32_t> requestMasks;
-
-        int presentFence = -1;
-
-        std::vector<Layer> releasedLayers;
-        std::vector<int> releaseFences;
-
-        uint32_t presentOrValidateState;
-
-        // Composer 2.4 implementation can return a client target property
-        // structure to indicate the client target properties that hardware
-        // composer requests. The composer client must change the client target
-        // properties to match this request.
-        IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
-                                                                   Dataspace::UNKNOWN};
-    };
-
-    std::vector<CommandError> mErrors;
-    std::unordered_map<Display, ReturnData> mReturnData;
-
-    // When SELECT_DISPLAY is parsed, this is updated to point to the
-    // display's return data in mReturnData.  We use it to avoid repeated
-    // map lookups.
-    ReturnData* mCurrentReturnData;
-};
-
-// Composer is a wrapper to IComposer, a proxy to server-side composer.
-class Composer final : public Hwc2::Composer {
-public:
-    explicit Composer(const std::string& serviceName);
-    ~Composer() override;
-
-    std::vector<IComposer::Capability> getCapabilities() override;
-    std::string dumpDebugInfo() override;
-
-    void registerCallback(const sp<IComposerCallback>& callback) override;
-
-    // Reset all pending commands in the command buffer. Useful if you want to
-    // skip a frame but have already queued some commands.
-    void resetCommands() override;
-
-    // Explicitly flush all pending commands in the command buffer.
-    Error executeCommands() override;
-
-    uint32_t getMaxVirtualDisplayCount() override;
-    Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                               Display* outDisplay) override;
-    Error destroyVirtualDisplay(Display display) override;
-
-    Error acceptDisplayChanges(Display display) override;
-
-    Error createLayer(Display display, Layer* outLayer) override;
-    Error destroyLayer(Display display, Layer layer) override;
-
-    Error getActiveConfig(Display display, Config* outConfig) override;
-    Error getChangedCompositionTypes(Display display, std::vector<Layer>* outLayers,
-                                     std::vector<IComposerClient::Composition>* outTypes) override;
-    Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
-    Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
-                              int32_t* outValue) override;
-    Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
-    Error getDisplayName(Display display, std::string* outName) override;
-
-    Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
-                             std::vector<Layer>* outLayers,
-                             std::vector<uint32_t>* outLayerRequestMasks) override;
-
-    Error getDozeSupport(Display display, bool* outSupport) override;
-    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
-                             float* outMaxAverageLuminance, float* outMinLuminance) override;
-
-    Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
-                           std::vector<int>* outReleaseFences) override;
-
-    Error presentDisplay(Display display, int* outPresentFence) override;
-
-    Error setActiveConfig(Display display, Config config) override;
-
-    /*
-     * The composer caches client targets internally.  When target is nullptr,
-     * the composer uses slot to look up the client target from its cache.
-     * When target is not nullptr, the cache is updated with the new target.
-     */
-    Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
-                          int acquireFence, Dataspace dataspace,
-                          const std::vector<IComposerClient::Rect>& damage) override;
-    Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
-    Error setColorTransform(Display display, const float* matrix, ColorTransform hint) override;
-    Error setOutputBuffer(Display display, const native_handle_t* buffer,
-                          int releaseFence) override;
-    Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
-    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
-    Error setClientTargetSlotCount(Display display) override;
-
-    Error validateDisplay(Display display, uint32_t* outNumTypes,
-                          uint32_t* outNumRequests) override;
-
-    Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes, uint32_t* outNumRequests,
-                                   int* outPresentFence, uint32_t* state) override;
-
-    Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
-    /* see setClientTarget for the purpose of slot */
-    Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
-                         const sp<GraphicBuffer>& buffer, int acquireFence) override;
-    Error setLayerSurfaceDamage(Display display, Layer layer,
-                                const std::vector<IComposerClient::Rect>& damage) override;
-    Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
-    Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
-    Error setLayerCompositionType(Display display, Layer layer,
-                                  IComposerClient::Composition type) override;
-    Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
-    Error setLayerDisplayFrame(Display display, Layer layer,
-                               const IComposerClient::Rect& frame) override;
-    Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
-    Error setLayerSidebandStream(Display display, Layer layer,
-                                 const native_handle_t* stream) override;
-    Error setLayerSourceCrop(Display display, Layer layer,
-                             const IComposerClient::FRect& crop) override;
-    Error setLayerTransform(Display display, Layer layer, Transform transform) override;
-    Error setLayerVisibleRegion(Display display, Layer layer,
-                                const std::vector<IComposerClient::Rect>& visible) override;
-    Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
-    // Composer HAL 2.2
-    Error setLayerPerFrameMetadata(
-            Display display, Layer layer,
-            const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
-    std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
-            Display display) override;
-    Error getRenderIntents(Display display, ColorMode colorMode,
-            std::vector<RenderIntent>* outRenderIntents) override;
-    Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
-
-    // Composer HAL 2.3
-    Error getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                       std::vector<uint8_t>* outData) override;
-    Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
-    Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
-                                                Dataspace* outDataspace,
-                                                uint8_t* outComponentMask) override;
-    Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
-                                           uint64_t maxFrames) override;
-    Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
-                                    DisplayedFrameStats* outStats) override;
-    Error setLayerPerFrameMetadataBlobs(
-            Display display, Layer layer,
-            const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
-    Error setDisplayBrightness(Display display, float brightness) override;
-
-    // Composer HAL 2.4
-    bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
-    Error getDisplayCapabilities(Display display,
-                                 std::vector<DisplayCapability>* outCapabilities) override;
-    V2_4::Error getDisplayConnectionType(Display display,
-                                         IComposerClient::DisplayConnectionType* outType) override;
-    V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
-    V2_4::Error setActiveConfigWithConstraints(
-            Display display, Config config,
-            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
-            VsyncPeriodChangeTimeline* outTimeline) override;
-    V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
-    V2_4::Error getSupportedContentTypes(
-            Display displayId,
-            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
-    V2_4::Error setContentType(Display displayId,
-                               IComposerClient::ContentType contentType) override;
-    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
-                                        bool mandatory, const std::vector<uint8_t>& value) override;
-    V2_4::Error getLayerGenericMetadataKeys(
-            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
-    Error getClientTargetProperty(
-            Display display,
-            IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
-
-private:
-    class CommandWriter : public CommandWriterBase {
-    public:
-        explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
-        ~CommandWriter() override {}
-    };
-
-    // Many public functions above simply write a command into the command
-    // queue to batch the calls.  validateDisplay and presentDisplay will call
-    // this function to execute the command queue.
-    Error execute();
-
-    sp<V2_1::IComposer> mComposer;
-
-    sp<V2_1::IComposerClient> mClient;
-    sp<V2_2::IComposerClient> mClient_2_2;
-    sp<V2_3::IComposerClient> mClient_2_3;
-    sp<IComposerClient> mClient_2_4;
-
-    // 64KiB minus a small space for metadata such as read/write pointers
-    static constexpr size_t kWriterInitialSize =
-        64 * 1024 / sizeof(uint32_t) - 16;
-    // Max number of buffers that may be cached for a given layer
-    // We obtain this number by:
-    // 1. Tightly coupling this cache to the max size of BufferQueue
-    // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
-    static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
-    CommandWriter mWriter;
-    CommandReader mReader;
-};
-
-} // namespace impl
-
-} // namespace Hwc2
-
-} // namespace android
-
-#endif // ANDROID_SF_COMPOSER_HAL_H
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 5de622b..0ab9605 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -16,9 +16,9 @@
 
 #pragma once
 
-#include "DisplayHardware/Hal.h"
-#include "Fps.h"
-#include "Scheduler/StrongTyping.h"
+#include <cstddef>
+#include <memory>
+#include <vector>
 
 #include <android-base/stringprintf.h>
 #include <android/configuration.h>
@@ -27,9 +27,10 @@
 #include <ui/Size.h>
 #include <utils/Timers.h>
 
-#include <cstddef>
-#include <memory>
-#include <vector>
+#include <scheduler/Fps.h>
+
+#include "DisplayHardware/Hal.h"
+#include "Scheduler/StrongTyping.h"
 
 namespace android {
 
@@ -161,4 +162,4 @@
                               mode.getDpiY(), mode.getGroup());
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 27146ab..34f2e76 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -37,7 +37,9 @@
 #include <iterator>
 #include <set>
 
-#include "ComposerHal.h"
+using aidl::android::hardware::graphics::composer3::Color;
+using aidl::android::hardware::graphics::composer3::Composition;
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
 
 namespace android {
 
@@ -142,12 +144,12 @@
 bool Display::isVsyncPeriodSwitchSupported() const {
     ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
 
-    return mComposer.isVsyncPeriodSwitchSupported();
+    return mComposer.isSupported(android::Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
 }
 
 Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
     std::vector<Hwc2::Layer> layerIds;
-    std::vector<Hwc2::IComposerClient::Composition> types;
+    std::vector<Composition> types;
     auto intError = mComposer.getChangedCompositionTypes(
             mId, &layerIds, &types);
     uint32_t numElements = layerIds.size();
@@ -283,8 +285,21 @@
     return Error::NONE;
 }
 
+bool Display::hasCapability(DisplayCapability capability) const {
+    std::scoped_lock lock(mDisplayCapabilitiesMutex);
+    if (mDisplayCapabilities) {
+        return mDisplayCapabilities->count(capability) > 0;
+    }
+
+    ALOGW("Can't query capability %s."
+          " Display Capabilities were not queried from HWC yet",
+          to_string(capability).c_str());
+
+    return false;
+}
+
 Error Display::supportsDoze(bool* outSupport) const {
-    *outSupport = mDisplayCapabilities.count(DisplayCapability::DOZE) > 0;
+    *outSupport = hasCapability(DisplayCapability::DOZE);
     return Error::NONE;
 }
 
@@ -421,8 +436,8 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::setColorTransform(const android::mat4& matrix, ColorTransform hint) {
-    auto intError = mComposer.setColorTransform(mId, matrix.asArray(), hint);
+Error Display::setColorTransform(const android::mat4& matrix) {
+    auto intError = mComposer.setColorTransform(mId, matrix.asArray());
     return static_cast<Error>(intError);
 }
 
@@ -443,21 +458,25 @@
 
     if (mode == PowerMode::ON) {
         std::call_once(mDisplayCapabilityQueryFlag, [this]() {
-            std::vector<Hwc2::DisplayCapability> tmpCapabilities;
+            std::vector<DisplayCapability> tmpCapabilities;
             auto error =
                     static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
             if (error == Error::NONE) {
+                std::scoped_lock lock(mDisplayCapabilitiesMutex);
+                mDisplayCapabilities.emplace();
                 for (auto capability : tmpCapabilities) {
-                    mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+                    mDisplayCapabilities->emplace(capability);
                 }
             } else if (error == Error::UNSUPPORTED) {
+                std::scoped_lock lock(mDisplayCapabilitiesMutex);
+                mDisplayCapabilities.emplace();
                 if (mCapabilities.count(Capability::SKIP_CLIENT_COLOR_TRANSFORM)) {
-                    mDisplayCapabilities.emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+                    mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
                 }
                 bool dozeSupport = false;
                 error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
                 if (error == Error::NONE && dozeSupport) {
-                    mDisplayCapabilities.emplace(DisplayCapability::DOZE);
+                    mDisplayCapabilities->emplace(DisplayCapability::DOZE);
                 }
             }
         });
@@ -473,11 +492,11 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests)
-{
+Error Display::validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+                        uint32_t* outNumRequests) {
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
-    auto intError = mComposer.validateDisplay(mId, &numTypes, &numRequests);
+    auto intError = mComposer.validateDisplay(mId, expectedPresentTime, &numTypes, &numRequests);
     auto error = static_cast<Error>(intError);
     if (error != Error::NONE && !hasChangesError(error)) {
         return error;
@@ -488,14 +507,14 @@
     return error;
 }
 
-Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
-                                 sp<android::Fence>* outPresentFence, uint32_t* state) {
-
+Error Display::presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+                                 uint32_t* outNumRequests, sp<android::Fence>* outPresentFence,
+                                 uint32_t* state) {
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
     int32_t presentFenceFd = -1;
-    auto intError = mComposer.presentOrValidateDisplay(
-            mId, &numTypes, &numRequests, &presentFenceFd, state);
+    auto intError = mComposer.presentOrValidateDisplay(mId, expectedPresentTime, &numTypes,
+                                                       &numRequests, &presentFenceFd, state);
     auto error = static_cast<Error>(intError);
     if (error != Error::NONE && !hasChangesError(error)) {
         return error;
@@ -512,9 +531,10 @@
     return error;
 }
 
-std::future<Error> Display::setDisplayBrightness(float brightness) {
-    return ftl::defer([composer = &mComposer, id = mId, brightness] {
-        const auto intError = composer->setDisplayBrightness(id, brightness);
+std::future<Error> Display::setDisplayBrightness(
+        float brightness, const Hwc2::Composer::DisplayBrightnessOptions& options) {
+    return ftl::defer([composer = &mComposer, id = mId, brightness, options] {
+        const auto intError = composer->setDisplayBrightness(id, brightness, options);
         return static_cast<Error>(intError);
     });
 }
@@ -538,8 +558,10 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
-    const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty,
+                                       float* outWhitePointNits) {
+    const auto error =
+            mComposer.getClientTargetProperty(mId, outClientTargetProperty, outWhitePointNits);
     return static_cast<Error>(error);
 }
 
@@ -562,6 +584,21 @@
 
 // Layer methods
 
+namespace {
+std::vector<Hwc2::IComposerClient::Rect> convertRegionToHwcRects(const Region& region) {
+    size_t rectCount = 0;
+    Rect const* rectArray = region.getArray(&rectCount);
+
+    std::vector<Hwc2::IComposerClient::Rect> hwcRects;
+    hwcRects.reserve(rectCount);
+    for (size_t rect = 0; rect < rectCount; ++rect) {
+        hwcRects.push_back({rectArray[rect].left, rectArray[rect].top, rectArray[rect].right,
+                            rectArray[rect].bottom});
+    }
+    return hwcRects;
+}
+} // namespace
+
 Layer::~Layer() = default;
 
 namespace impl {
@@ -652,15 +689,7 @@
         intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId,
                                                    std::vector<Hwc2::IComposerClient::Rect>());
     } else {
-        size_t rectCount = 0;
-        auto rectArray = damage.getArray(&rectCount);
-
-        std::vector<Hwc2::IComposerClient::Rect> hwcRects;
-        for (size_t rect = 0; rect < rectCount; ++rect) {
-            hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
-                    rectArray[rect].right, rectArray[rect].bottom});
-        }
-
+        const auto hwcRects = convertRegionToHwcRects(damage);
         intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId, hwcRects);
     }
 
@@ -848,16 +877,7 @@
         return Error::NONE;
     }
     mVisibleRegion = region;
-
-    size_t rectCount = 0;
-    auto rectArray = region.getArray(&rectCount);
-
-    std::vector<Hwc2::IComposerClient::Rect> hwcRects;
-    for (size_t rect = 0; rect < rectCount; ++rect) {
-        hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
-                rectArray[rect].right, rectArray[rect].bottom});
-    }
-
+    const auto hwcRects = convertRegionToHwcRects(region);
     auto intError = mComposer.setLayerVisibleRegion(mDisplay->getId(), mId, hwcRects);
     return static_cast<Error>(intError);
 }
@@ -902,6 +922,31 @@
     return static_cast<Error>(intError);
 }
 
+// AIDL HAL
+Error Layer::setWhitePointNits(float whitePointNits) {
+    if (CC_UNLIKELY(!mDisplay)) {
+        return Error::BAD_DISPLAY;
+    }
+
+    auto intError = mComposer.setLayerWhitePointNits(mDisplay->getId(), mId, whitePointNits);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setBlockingRegion(const Region& region) {
+    if (CC_UNLIKELY(!mDisplay)) {
+        return Error::BAD_DISPLAY;
+    }
+
+    if (region.isRect() && mBlockingRegion.isRect() &&
+        (region.getBounds() == mBlockingRegion.getBounds())) {
+        return Error::NONE;
+    }
+    mBlockingRegion = region;
+    const auto hwcRects = convertRegionToHwcRects(region);
+    const auto intError = mComposer.setLayerBlockingRegion(mDisplay->getId(), mId, hwcRects);
+    return static_cast<Error>(intError);
+}
+
 } // namespace impl
 } // namespace HWC2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 871465d..01a482d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android-base/expected.h>
+#include <android-base/thread_annotations.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/HdrCapabilities.h>
@@ -33,8 +34,13 @@
 #include <unordered_set>
 #include <vector>
 
+#include "ComposerHal.h"
 #include "Hal.h"
 
+#include <aidl/android/hardware/graphics/composer3/Color.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+
 namespace android {
 
 class Fence;
@@ -79,7 +85,8 @@
     virtual hal::HWDisplayId getId() const = 0;
     virtual bool isConnected() const = 0;
     virtual void setConnected(bool connected) = 0; // For use by Device only
-    virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0;
+    virtual bool hasCapability(
+            aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0;
     virtual bool isVsyncPeriodSwitchSupported() const = 0;
     virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
 
@@ -87,7 +94,8 @@
     [[clang::warn_unused_result]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
     createLayer() = 0;
     [[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes(
-            std::unordered_map<Layer*, hal::Composition>* outTypes) = 0;
+            std::unordered_map<Layer*, aidl::android::hardware::graphics::composer3::Composition>*
+                    outTypes) = 0;
     [[clang::warn_unused_result]] virtual hal::Error getColorModes(
             std::vector<hal::ColorMode>* outModes) const = 0;
     // Returns a bitmask which contains HdrMetadata::Type::*.
@@ -124,19 +132,20 @@
     [[clang::warn_unused_result]] virtual hal::Error setColorMode(
             hal::ColorMode mode, hal::RenderIntent renderIntent) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setColorTransform(
-            const android::mat4& matrix, hal::ColorTransform hint) = 0;
+            const android::mat4& matrix) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setOutputBuffer(
             const android::sp<android::GraphicBuffer>& buffer,
             const android::sp<android::Fence>& releaseFence) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setPowerMode(hal::PowerMode mode) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setVsyncEnabled(hal::Vsync enabled) = 0;
-    [[clang::warn_unused_result]] virtual hal::Error validate(uint32_t* outNumTypes,
+    [[clang::warn_unused_result]] virtual hal::Error validate(nsecs_t expectedPresentTime,
+                                                              uint32_t* outNumTypes,
                                                               uint32_t* outNumRequests) = 0;
     [[clang::warn_unused_result]] virtual hal::Error presentOrValidate(
-            uint32_t* outNumTypes, uint32_t* outNumRequests,
+            nsecs_t expectedPresentTime, uint32_t* outNumTypes, uint32_t* outNumRequests,
             android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
     [[clang::warn_unused_result]] virtual std::future<hal::Error> setDisplayBrightness(
-            float brightness) = 0;
+            float brightness, const Hwc2::Composer::DisplayBrightnessOptions& options) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setActiveConfigWithConstraints(
             hal::HWConfigId configId, const hal::VsyncPeriodChangeConstraints& constraints,
             hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
@@ -145,7 +154,7 @@
             std::vector<hal::ContentType>*) const = 0;
     [[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
     [[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
-            hal::ClientTargetProperty* outClientTargetProperty) = 0;
+            hal::ClientTargetProperty* outClientTargetProperty, float* outWhitePointNits) = 0;
 };
 
 namespace impl {
@@ -162,7 +171,9 @@
     hal::Error acceptChanges() override;
     base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> createLayer() override;
     hal::Error getChangedCompositionTypes(
-            std::unordered_map<HWC2::Layer*, hal::Composition>* outTypes) override;
+            std::unordered_map<HWC2::Layer*,
+                               aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+            override;
     hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override;
     // Returns a bitmask which contains HdrMetadata::Type::*.
     int32_t getSupportedPerFrameMetadata() const override;
@@ -175,7 +186,7 @@
             hal::DisplayRequest* outDisplayRequests,
             std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override;
     hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
-    hal::Error supportsDoze(bool* outSupport) const override;
+    hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex);
     hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
     hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
                                                      hal::Dataspace* outDataspace,
@@ -191,16 +202,19 @@
                                const android::sp<android::Fence>& acquireFence,
                                hal::Dataspace dataspace) override;
     hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override;
-    hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override;
+    hal::Error setColorTransform(const android::mat4& matrix) override;
     hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&,
                                const android::sp<android::Fence>& releaseFence) override;
     hal::Error setPowerMode(hal::PowerMode) override;
     hal::Error setVsyncEnabled(hal::Vsync enabled) override;
-    hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
-    hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+    hal::Error validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+                        uint32_t* outNumRequests) override;
+    hal::Error presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+                                 uint32_t* outNumRequests,
                                  android::sp<android::Fence>* outPresentFence,
                                  uint32_t* state) override;
-    std::future<hal::Error> setDisplayBrightness(float brightness) override;
+    std::future<hal::Error> setDisplayBrightness(
+            float brightness, const Hwc2::Composer::DisplayBrightnessOptions& options) override;
     hal::Error setActiveConfigWithConstraints(hal::HWConfigId configId,
                                               const hal::VsyncPeriodChangeConstraints& constraints,
                                               hal::VsyncPeriodChangeTimeline* outTimeline) override;
@@ -208,15 +222,15 @@
     hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>* outSupportedContentTypes) const override;
     hal::Error setContentType(hal::ContentType) override;
-    hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
+    hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty,
+                                       float* outWhitePointNits) override;
 
     // Other Display methods
     hal::HWDisplayId getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
     void setConnected(bool connected) override; // For use by Device only
-    const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override {
-        return mDisplayCapabilities;
-    };
+    bool hasCapability(aidl::android::hardware::graphics::composer3::DisplayCapability)
+            const override EXCLUDES(mDisplayCapabilitiesMutex);
     bool isVsyncPeriodSwitchSupported() const override;
     void onLayerDestroyed(hal::HWLayerId layerId) override;
 
@@ -243,8 +257,11 @@
     using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>;
     Layers mLayers;
 
+    mutable std::mutex mDisplayCapabilitiesMutex;
     std::once_flag mDisplayCapabilityQueryFlag;
-    std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
+    std::optional<
+            std::unordered_set<aidl::android::hardware::graphics::composer3::DisplayCapability>>
+            mDisplayCapabilities GUARDED_BY(mDisplayCapabilitiesMutex);
 };
 
 } // namespace impl
@@ -263,8 +280,10 @@
             const android::Region& damage) = 0;
 
     [[clang::warn_unused_result]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
-    [[clang::warn_unused_result]] virtual hal::Error setColor(hal::Color color) = 0;
-    [[clang::warn_unused_result]] virtual hal::Error setCompositionType(hal::Composition type) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setColor(
+            aidl::android::hardware::graphics::composer3::Color color) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setCompositionType(
+            aidl::android::hardware::graphics::composer3::Composition type) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setDataspace(hal::Dataspace dataspace) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setPerFrameMetadata(
             const int32_t supportedPerFrameMetadata, const android::HdrMetadata& metadata) = 0;
@@ -287,6 +306,11 @@
     // Composer HAL 2.4
     [[clang::warn_unused_result]] virtual hal::Error setLayerGenericMetadata(
             const std::string& name, bool mandatory, const std::vector<uint8_t>& value) = 0;
+
+    // AIDL HAL
+    [[clang::warn_unused_result]] virtual hal::Error setWhitePointNits(float whitePointNits) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setBlockingRegion(
+            const android::Region& region) = 0;
 };
 
 namespace impl {
@@ -310,8 +334,9 @@
     hal::Error setSurfaceDamage(const android::Region& damage) override;
 
     hal::Error setBlendMode(hal::BlendMode mode) override;
-    hal::Error setColor(hal::Color color) override;
-    hal::Error setCompositionType(hal::Composition type) override;
+    hal::Error setColor(aidl::android::hardware::graphics::composer3::Color color) override;
+    hal::Error setCompositionType(
+            aidl::android::hardware::graphics::composer3::Composition type) override;
     hal::Error setDataspace(hal::Dataspace dataspace) override;
     hal::Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
                                    const android::HdrMetadata& metadata) override;
@@ -330,6 +355,10 @@
     hal::Error setLayerGenericMetadata(const std::string& name, bool mandatory,
                                        const std::vector<uint8_t>& value) override;
 
+    // AIDL HAL
+    hal::Error setWhitePointNits(float whitePointNits) override;
+    hal::Error setBlockingRegion(const android::Region& region) override;
+
 private:
     // These are references to data owned by HWC2::Device, which will outlive
     // this HWC2::Layer, so these references are guaranteed to be valid for
@@ -344,6 +373,7 @@
     // multiple times.
     android::Region mVisibleRegion = android::Region::INVALID_REGION;
     android::Region mDamageRegion = android::Region::INVALID_REGION;
+    android::Region mBlockingRegion = android::Region::INVALID_REGION;
     hal::Dataspace mDataSpace = hal::Dataspace::UNKNOWN;
     android::HdrMetadata mHdrMetadata;
     android::mat4 mColorMatrix;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index a790b4c..057db46 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -143,7 +143,7 @@
                 sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
 
 HWComposer::HWComposer(const std::string& composerServiceName)
-      : HWComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {}
+      : HWComposer(Hwc2::Composer::create(composerServiceName)) {}
 
 HWComposer::~HWComposer() {
     mDisplayData.clear();
@@ -159,8 +159,10 @@
     }
     mRegisteredCallback = true;
 
+    const bool vsyncSwitchingSupported =
+            mComposer->isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
     mComposer->registerCallback(
-            sp<ComposerCallbackBridge>::make(callback, mComposer->isVsyncPeriodSwitchSupported()));
+            sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported));
 }
 
 bool HWComposer::getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
@@ -180,10 +182,11 @@
     return mCapabilities.count(capability) > 0;
 }
 
-bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
-                                      hal::DisplayCapability capability) const {
+bool HWComposer::hasDisplayCapability(
+        HalDisplayId displayId,
+        aidl::android::hardware::graphics::composer3::DisplayCapability capability) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
+    return mDisplayData.at(displayId).hwcDisplay->hasCapability(capability);
 }
 
 std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId,
@@ -212,8 +215,6 @@
     RETURN_IF_INVALID_DISPLAY(*displayId, false);
 
     auto& displayData = mDisplayData[*displayId];
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(*displayId).c_str());
 
     {
         // There have been reports of HWCs that signal several vsync events
@@ -271,7 +272,6 @@
     display->setConnected(true);
     auto& displayData = mDisplayData[displayId];
     displayData.hwcDisplay = std::move(display);
-    displayData.isVirtual = true;
     return true;
 }
 
@@ -279,10 +279,8 @@
                                          PhysicalDisplayId displayId) {
     mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
 
-    if (!mInternalHwcDisplayId) {
-        mInternalHwcDisplayId = hwcDisplayId;
-    } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
-        mExternalHwcDisplayId = hwcDisplayId;
+    if (!mPrimaryHwcDisplayId) {
+        mPrimaryHwcDisplayId = hwcDisplayId;
     }
 
     auto& displayData = mDisplayData[displayId];
@@ -372,7 +370,7 @@
     ui::DisplayConnectionType type;
     const auto error = hwcDisplay->getConnectionType(&type);
 
-    const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId
+    const auto FALLBACK_TYPE = hwcDisplay->getId() == mPrimaryHwcDisplayId
             ? ui::DisplayConnectionType::Internal
             : ui::DisplayConnectionType::External;
 
@@ -428,9 +426,6 @@
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     // NOTE: we use our own internal lock here because we have to call
     // into the HWC with the lock held, and we want to make sure
     // that even if HWC blocks (which it shouldn't), it won't
@@ -465,7 +460,7 @@
 status_t HWComposer::getDeviceCompositionChanges(
         HalDisplayId displayId, bool frameUsesClientComposition,
         std::chrono::steady_clock::time_point earliestPresentTime,
-        const std::shared_ptr<FenceTime>& previousPresentFence,
+        const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
@@ -487,16 +482,30 @@
     // earliest time to present. Otherwise, we may present a frame too early.
     // 2. There is no client composition. Otherwise, we first need to render the
     // client target buffer.
-    const bool prevFencePending =
-            previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
-    const bool canPresentEarly =
-            !prevFencePending && std::chrono::steady_clock::now() < earliestPresentTime;
-    const bool canSkipValidate = !canPresentEarly && !frameUsesClientComposition;
+    const bool canSkipValidate = [&] {
+        // We must call validate if we have client composition
+        if (frameUsesClientComposition) {
+            return false;
+        }
+
+        // If composer supports getting the expected present time, we can skip
+        // as composer will make sure to prevent early presentation
+        if (mComposer->isSupported(Hwc2::Composer::OptionalFeature::ExpectedPresentTime)) {
+            return true;
+        }
+
+        // composer doesn't support getting the expected present time. We can only
+        // skip validate if we know that we are not going to present early.
+        return std::chrono::steady_clock::now() >= earliestPresentTime ||
+                previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
+    }();
+
     displayData.validateWasSkipped = false;
     if (canSkipValidate) {
         sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
-        error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
+        error = hwcDisplay->presentOrValidate(expectedPresentTime, &numTypes, &numRequests,
+                                              &outPresentFence, &state);
         if (!hasChangesError(error)) {
             RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);
         }
@@ -511,7 +520,7 @@
         }
         // Present failed but Validate ran.
     } else {
-        error = hwcDisplay->validate(&numTypes, &numRequests);
+        error = hwcDisplay->validate(expectedPresentTime, &numTypes, &numRequests);
     }
     ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
     if (!hasChangesError(error)) {
@@ -530,11 +539,13 @@
     RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
 
     DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
-    error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
+    float clientTargetWhitePointNits;
+    error = hwcDisplay->getClientTargetProperty(&clientTargetProperty, &clientTargetWhitePointNits);
 
     outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
                                                std::move(layerRequests),
-                                               std::move(clientTargetProperty)});
+                                               std::move(clientTargetProperty),
+                                               clientTargetWhitePointNits});
     error = hwcDisplay->acceptChanges();
     RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
 
@@ -575,9 +586,11 @@
         return NO_ERROR;
     }
 
-    const bool previousFramePending =
-            previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
-    if (!previousFramePending) {
+    const bool waitForEarliestPresent =
+            !mComposer->isSupported(Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+            previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+
+    if (waitForEarliestPresent) {
         ATRACE_NAME("wait for earliest present time");
         std::this_thread::sleep_until(earliestPresentTime);
     }
@@ -597,14 +610,11 @@
 status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
-    const auto& displayData = mDisplayData[displayId];
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     if (mode == hal::PowerMode::OFF) {
         setVsyncEnabled(displayId, hal::Vsync::DISABLE);
     }
 
+    const auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
     switch (mode) {
         case hal::PowerMode::OFF:
@@ -664,11 +674,7 @@
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
-    bool isIdentity = transform == mat4();
-    auto error = displayData.hwcDisplay
-                         ->setColorTransform(transform,
-                                             isIdentity ? hal::ColorTransform::IDENTITY
-                                                        : hal::ColorTransform::ARBITRARY_MATRIX);
+    auto error = displayData.hwcDisplay->setColorTransform(transform);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
@@ -678,14 +684,15 @@
     auto& displayData = mDisplayData[displayId];
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
 
-    // TODO(b/74619554): Select internal/external display from remaining displays.
-    if (hwcDisplayId == mInternalHwcDisplayId) {
-        mInternalHwcDisplayId.reset();
-    } else if (hwcDisplayId == mExternalHwcDisplayId) {
-        mExternalHwcDisplayId.reset();
-    }
     mPhysicalDisplayIdMap.erase(hwcDisplayId);
     mDisplayData.erase(displayId);
+
+    // Reset the primary display ID if we're disconnecting it.
+    // This way isHeadless() will return false, which is necessary
+    // because getPrimaryDisplayId() will crash.
+    if (mPrimaryHwcDisplayId == hwcDisplayId) {
+        mPrimaryHwcDisplayId.reset();
+    }
 }
 
 status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence,
@@ -693,9 +700,6 @@
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto& displayData = mDisplayData[displayId];
 
-    LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
@@ -778,12 +782,13 @@
     return NO_ERROR;
 }
 
-std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId,
-                                                       float brightness) {
+std::future<status_t> HWComposer::setDisplayBrightness(
+        PhysicalDisplayId displayId, float brightness,
+        const Hwc2::Composer::DisplayBrightnessOptions& options) {
     RETURN_IF_INVALID_DISPLAY(displayId, ftl::yield<status_t>(BAD_INDEX));
     auto& display = mDisplayData[displayId].hwcDisplay;
 
-    return ftl::chain(display->setDisplayBrightness(brightness))
+    return ftl::chain(display->setDisplayBrightness(brightness, options))
             .then([displayId](hal::Error error) -> status_t {
                 if (error == hal::Error::UNSUPPORTED) {
                     RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
@@ -853,8 +858,7 @@
 
 std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(
         PhysicalDisplayId displayId) const {
-    if (const auto it = mDisplayData.find(displayId);
-        it != mDisplayData.end() && !it->second.isVirtual) {
+    if (const auto it = mDisplayData.find(displayId); it != mDisplayData.end()) {
         return it->second.hwcDisplay->getId();
     }
     return {};
@@ -868,7 +872,8 @@
         return true;
     }
 
-    if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
+    // Legacy mode only supports IDs LEGACY_DISPLAY_TYPE_PRIMARY and LEGACY_DISPLAY_TYPE_EXTERNAL.
+    if (!mHasMultiDisplaySupport && mPhysicalDisplayIdMap.size() == 2) {
         ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
         return true;
     }
@@ -909,7 +914,7 @@
         }
 
         info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
-            const bool isPrimary = !mInternalHwcDisplayId;
+            const bool isPrimary = !mPrimaryHwcDisplayId;
             if (mHasMultiDisplaySupport) {
                 if (const auto info = parseDisplayIdentificationData(port, data)) {
                     return *info;
@@ -922,8 +927,8 @@
             }
 
             return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
-                                             .name = isPrimary ? "Internal display"
-                                                               : "External display",
+                                             .name = isPrimary ? "Primary display"
+                                                               : "Secondary display",
                                              .deviceProductInfo = std::nullopt};
         }();
     }
@@ -936,9 +941,12 @@
 
 std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
         hal::HWDisplayId hwcDisplayId) {
+    LOG_ALWAYS_FATAL_IF(hwcDisplayId == mPrimaryHwcDisplayId,
+                        "Primary display cannot be disconnected.");
+
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
-        ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
         return {};
     }
 
@@ -947,7 +955,7 @@
     if (isConnected(*displayId)) {
         mDisplayData[*displayId].hwcDisplay->setConnected(false);
     } else {
-        ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected");
     }
     // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
     // via SurfaceFlinger's onHotplugReceived callback handling
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 49f96d9..4fae06d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_HWCOMPOSER_H
-#define ANDROID_SF_HWCOMPOSER_H
+#pragma once
 
 #include <cstdint>
 #include <future>
@@ -44,6 +43,9 @@
 #include "HWC2.h"
 #include "Hal.h"
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+
 namespace android {
 
 namespace hal = hardware::graphics::composer::hal;
@@ -71,7 +73,9 @@
 class HWComposer {
 public:
     struct DeviceRequestedChanges {
-        using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
+        using ChangedTypes =
+                std::unordered_map<HWC2::Layer*,
+                                   aidl::android::hardware::graphics::composer3::Composition>;
         using ClientTargetProperty = hal::ClientTargetProperty;
         using DisplayRequests = hal::DisplayRequest;
         using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
@@ -80,6 +84,7 @@
         DisplayRequests displayRequests;
         LayerRequests layerRequests;
         ClientTargetProperty clientTargetProperty;
+        float clientTargetWhitePointNits;
     };
 
     struct HWCDisplayMode {
@@ -106,7 +111,9 @@
                                               DisplayIdentificationData* outData) const = 0;
 
     virtual bool hasCapability(hal::Capability) const = 0;
-    virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0;
+    virtual bool hasDisplayCapability(
+            HalDisplayId,
+            aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0;
 
     virtual size_t getMaxVirtualDisplayCount() const = 0;
     virtual size_t getMaxVirtualDisplayDimension() const = 0;
@@ -130,7 +137,7 @@
     virtual status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
             std::chrono::steady_clock::time_point earliestPresentTime,
-            const std::shared_ptr<FenceTime>& previousPresentFence,
+            const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
             std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
     virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
@@ -185,7 +192,9 @@
                                                DisplayedFrameStats* outStats) = 0;
 
     // Sets the brightness of a display.
-    virtual std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) = 0;
+    virtual std::future<status_t> setDisplayBrightness(
+            PhysicalDisplayId, float brightness,
+            const Hwc2::Composer::DisplayBrightnessOptions&) = 0;
 
     // Events handling ---------------------------------------------------------
 
@@ -228,14 +237,21 @@
     virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
             const = 0;
 
-    // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
 
     virtual Hwc2::Composer* getComposer() const = 0;
 
-    // TODO(b/74619554): Remove special cases for internal/external display.
-    virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
-    virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
+    // Returns the first display connected at boot. Its connection via HWComposer::onHotplug,
+    // which in practice is immediately after HWComposer construction, must occur before any
+    // call to this function.
+    // The primary display can be temporarily disconnected from the perspective
+    // of this class. Callers must not call getPrimaryHwcDisplayId() or getPrimaryDisplayId()
+    // if isHeadless().
+    //
+    // TODO(b/182939859): Remove special cases for primary display.
+    virtual hal::HWDisplayId getPrimaryHwcDisplayId() const = 0;
+    virtual PhysicalDisplayId getPrimaryDisplayId() const = 0;
+    virtual bool isHeadless() const = 0;
 
     virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
     virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
@@ -256,7 +272,9 @@
                                       DisplayIdentificationData* outData) const override;
 
     bool hasCapability(hal::Capability) const override;
-    bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override;
+    bool hasDisplayCapability(
+            HalDisplayId,
+            aidl::android::hardware::graphics::composer3::DisplayCapability) const override;
 
     size_t getMaxVirtualDisplayCount() const override;
     size_t getMaxVirtualDisplayDimension() const override;
@@ -272,7 +290,7 @@
     status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
             std::chrono::steady_clock::time_point earliestPresentTime,
-            const std::shared_ptr<FenceTime>& previousPresentFence,
+            const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
             std::optional<DeviceRequestedChanges>* outChanges) override;
 
     status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
@@ -324,7 +342,9 @@
                                               uint64_t maxFrames) override;
     status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
                                        DisplayedFrameStats* outStats) override;
-    std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) override;
+    std::future<status_t> setDisplayBrightness(
+            PhysicalDisplayId, float brightness,
+            const Hwc2::Composer::DisplayBrightnessOptions&) override;
 
     // Events handling ---------------------------------------------------------
 
@@ -366,14 +386,19 @@
 
     Hwc2::Composer* getComposer() const override { return mComposer.get(); }
 
-    // TODO(b/74619554): Remove special cases for internal/external display.
-    std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const override {
-        return mInternalHwcDisplayId;
+    hal::HWDisplayId getPrimaryHwcDisplayId() const override {
+        LOG_ALWAYS_FATAL_IF(!mPrimaryHwcDisplayId, "Missing HWC primary display");
+        return *mPrimaryHwcDisplayId;
     }
-    std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const override {
-        return mExternalHwcDisplayId;
+
+    PhysicalDisplayId getPrimaryDisplayId() const override {
+        const auto id = toPhysicalDisplayId(getPrimaryHwcDisplayId());
+        LOG_ALWAYS_FATAL_IF(!id, "Missing primary display");
+        return *id;
     }
 
+    virtual bool isHeadless() const override { return !mPrimaryHwcDisplayId; }
+
     std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override;
     std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override;
 
@@ -382,12 +407,9 @@
     friend TestableSurfaceFlinger;
 
     struct DisplayData {
-        bool isVirtual = false;
         std::unique_ptr<HWC2::Display> hwcDisplay;
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
-        buffer_handle_t outbufHandle = nullptr;
-        sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
 
         bool validateWasSkipped;
         hal::Error presentError;
@@ -418,8 +440,7 @@
     bool mRegisteredCallback = false;
 
     std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap;
-    std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
-    std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
+    std::optional<hal::HWDisplayId> mPrimaryHwcDisplayId;
     bool mHasMultiDisplaySupport = false;
 
     const size_t mMaxVirtualDisplayDimension;
@@ -428,5 +449,3 @@
 
 } // namespace impl
 } // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 02d0658..40c9761 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,9 @@
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+
 #define ERROR_HAS_CHANGES 5
 
 namespace android {
@@ -48,13 +51,10 @@
 
 using Attribute = IComposerClient::Attribute;
 using BlendMode = IComposerClient::BlendMode;
-using Color = IComposerClient::Color;
-using Composition = IComposerClient::Composition;
 using Connection = IComposerCallback::Connection;
 using ContentType = IComposerClient::ContentType;
 using Capability = IComposer::Capability;
 using ClientTargetProperty = IComposerClient::ClientTargetProperty;
-using DisplayCapability = IComposerClient::DisplayCapability;
 using DisplayRequest = IComposerClient::DisplayRequest;
 using DisplayType = IComposerClient::DisplayType;
 using HWConfigId = V2_1::Config;
@@ -95,20 +95,48 @@
     }
 }
 
-inline std::string to_string(hardware::graphics::composer::hal::Composition composition) {
+inline std::string to_string(
+        aidl::android::hardware::graphics::composer3::Composition composition) {
     switch (composition) {
-        case hardware::graphics::composer::hal::Composition::INVALID:
+        case aidl::android::hardware::graphics::composer3::Composition::INVALID:
             return "Invalid";
-        case hardware::graphics::composer::hal::Composition::CLIENT:
+        case aidl::android::hardware::graphics::composer3::Composition::CLIENT:
             return "Client";
-        case hardware::graphics::composer::hal::Composition::DEVICE:
+        case aidl::android::hardware::graphics::composer3::Composition::DEVICE:
             return "Device";
-        case hardware::graphics::composer::hal::Composition::SOLID_COLOR:
+        case aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR:
             return "SolidColor";
-        case hardware::graphics::composer::hal::Composition::CURSOR:
+        case aidl::android::hardware::graphics::composer3::Composition::CURSOR:
             return "Cursor";
-        case hardware::graphics::composer::hal::Composition::SIDEBAND:
+        case aidl::android::hardware::graphics::composer3::Composition::SIDEBAND:
             return "Sideband";
+        case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
+            return "DisplayDecoration";
+        default:
+            return "Unknown";
+    }
+}
+
+inline std::string to_string(
+        aidl::android::hardware::graphics::composer3::DisplayCapability displayCapability) {
+    switch (displayCapability) {
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::INVALID:
+            return "Invalid";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::
+                SKIP_CLIENT_COLOR_TRANSFORM:
+            return "SkipColorTransform";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::DOZE:
+            return "Doze";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::BRIGHTNESS:
+            return "Brightness";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::PROTECTED_CONTENTS:
+            return "ProtectedContents";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::AUTO_LOW_LATENCY_MODE:
+            return "AutoLowLatencyMode";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::SUSPEND:
+            return "Suspend";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::DISPLAY_DECORATION:
+            return "DisplayDecoration";
         default:
             return "Unknown";
     }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
new file mode 100644
index 0000000..0ab1cfb
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -0,0 +1,1545 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "HwcComposer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "HidlComposerHal.h"
+
+#include <android/binder_manager.h>
+#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/HidlTransportUtils.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+#include "Hal.h"
+
+#include <algorithm>
+#include <cinttypes>
+
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_vec;
+using hardware::Return;
+
+namespace Hwc2 {
+
+HidlComposer::~HidlComposer() = default;
+
+namespace {
+
+class BufferHandle {
+public:
+    explicit BufferHandle(const native_handle_t* buffer) {
+        // nullptr is not a valid handle to HIDL
+        mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
+    }
+
+    operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
+    {
+        return mHandle;
+    }
+
+private:
+    NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 0, 0);
+    hidl_handle mHandle;
+};
+
+class FenceHandle {
+public:
+    FenceHandle(int fd, bool owned) : mOwned(owned) {
+        native_handle_t* handle;
+        if (fd >= 0) {
+            handle = native_handle_init(mStorage, 1, 0);
+            handle->data[0] = fd;
+        } else {
+            // nullptr is not a valid handle to HIDL
+            handle = native_handle_init(mStorage, 0, 0);
+        }
+        mHandle = handle;
+    }
+
+    ~FenceHandle() {
+        if (mOwned) {
+            native_handle_close(mHandle);
+        }
+    }
+
+    operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
+    {
+        return mHandle;
+    }
+
+private:
+    bool mOwned;
+    NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 1, 0);
+    hidl_handle mHandle;
+};
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
+
+template <typename T, typename U>
+T unwrapRet(Return<T>& ret, const U& default_val) {
+    return (ret.isOk()) ? static_cast<T>(ret) : static_cast<T>(default_val);
+}
+
+Error unwrapRet(Return<Error>& ret) {
+    return unwrapRet(ret, kDefaultError);
+}
+
+} // anonymous namespace
+
+HidlComposer::HidlComposer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
+    mComposer = V2_1::IComposer::getService(serviceName);
+
+    if (mComposer == nullptr) {
+        LOG_ALWAYS_FATAL("failed to get hwcomposer service");
+    }
+
+    if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
+        composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
+            if (tmpError == V2_4::Error::NONE) {
+                mClient = tmpClient;
+                mClient_2_2 = tmpClient;
+                mClient_2_3 = tmpClient;
+                mClient_2_4 = tmpClient;
+            }
+        });
+    } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
+        composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
+            if (tmpError == Error::NONE) {
+                mClient = tmpClient;
+                mClient_2_2 = tmpClient;
+                mClient_2_3 = tmpClient;
+            }
+        });
+    } else {
+        mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
+            if (tmpError != Error::NONE) {
+                return;
+            }
+
+            mClient = tmpClient;
+            if (sp<V2_2::IComposer> composer_2_2 = V2_2::IComposer::castFrom(mComposer)) {
+                mClient_2_2 = V2_2::IComposerClient::castFrom(mClient);
+                LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr,
+                                    "IComposer 2.2 did not return IComposerClient 2.2");
+            }
+        });
+    }
+
+    if (mClient == nullptr) {
+        LOG_ALWAYS_FATAL("failed to create composer client");
+    }
+}
+
+bool HidlComposer::isSupported(OptionalFeature feature) const {
+    switch (feature) {
+        case OptionalFeature::RefreshRateSwitching:
+            return mClient_2_4 != nullptr;
+        case OptionalFeature::ExpectedPresentTime:
+        case OptionalFeature::DisplayBrightnessCommand:
+            return false;
+    }
+}
+
+std::vector<IComposer::Capability> HidlComposer::getCapabilities() {
+    std::vector<IComposer::Capability> capabilities;
+    mComposer->getCapabilities(
+            [&](const auto& tmpCapabilities) { capabilities = tmpCapabilities; });
+    return capabilities;
+}
+
+std::string HidlComposer::dumpDebugInfo() {
+    std::string info;
+    mComposer->dumpDebugInfo([&](const auto& tmpInfo) { info = tmpInfo.c_str(); });
+
+    return info;
+}
+
+void HidlComposer::registerCallback(const sp<IComposerCallback>& callback) {
+    android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
+
+    auto ret = [&]() {
+        if (mClient_2_4) {
+            return mClient_2_4->registerCallback_2_4(callback);
+        }
+        return mClient->registerCallback(callback);
+    }();
+    if (!ret.isOk()) {
+        ALOGE("failed to register IComposerCallback");
+    }
+}
+
+void HidlComposer::resetCommands() {
+    mWriter.reset();
+}
+
+Error HidlComposer::executeCommands() {
+    return execute();
+}
+
+uint32_t HidlComposer::getMaxVirtualDisplayCount() {
+    auto ret = mClient->getMaxVirtualDisplayCount();
+    return unwrapRet(ret, 0);
+}
+
+Error HidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+                                         Display* outDisplay) {
+    const uint32_t bufferSlotCount = 1;
+    Error error = kDefaultError;
+    if (mClient_2_2) {
+        mClient_2_2->createVirtualDisplay_2_2(width, height,
+                                              static_cast<types::V1_1::PixelFormat>(*format),
+                                              bufferSlotCount,
+                                              [&](const auto& tmpError, const auto& tmpDisplay,
+                                                  const auto& tmpFormat) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
+
+                                                  *outDisplay = tmpDisplay;
+                                                  *format = static_cast<types::V1_2::PixelFormat>(
+                                                          tmpFormat);
+                                              });
+    } else {
+        mClient->createVirtualDisplay(width, height, static_cast<types::V1_0::PixelFormat>(*format),
+                                      bufferSlotCount,
+                                      [&](const auto& tmpError, const auto& tmpDisplay,
+                                          const auto& tmpFormat) {
+                                          error = tmpError;
+                                          if (error != Error::NONE) {
+                                              return;
+                                          }
+
+                                          *outDisplay = tmpDisplay;
+                                          *format = static_cast<PixelFormat>(tmpFormat);
+                                      });
+    }
+
+    return error;
+}
+
+Error HidlComposer::destroyVirtualDisplay(Display display) {
+    auto ret = mClient->destroyVirtualDisplay(display);
+    return unwrapRet(ret);
+}
+
+Error HidlComposer::acceptDisplayChanges(Display display) {
+    mWriter.selectDisplay(display);
+    mWriter.acceptDisplayChanges();
+    return Error::NONE;
+}
+
+Error HidlComposer::createLayer(Display display, Layer* outLayer) {
+    Error error = kDefaultError;
+    mClient->createLayer(display, kMaxLayerBufferCount,
+                         [&](const auto& tmpError, const auto& tmpLayer) {
+                             error = tmpError;
+                             if (error != Error::NONE) {
+                                 return;
+                             }
+
+                             *outLayer = tmpLayer;
+                         });
+
+    return error;
+}
+
+Error HidlComposer::destroyLayer(Display display, Layer layer) {
+    auto ret = mClient->destroyLayer(display, layer);
+    return unwrapRet(ret);
+}
+
+Error HidlComposer::getActiveConfig(Display display, Config* outConfig) {
+    Error error = kDefaultError;
+    mClient->getActiveConfig(display, [&](const auto& tmpError, const auto& tmpConfig) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outConfig = tmpConfig;
+    });
+
+    return error;
+}
+
+Error HidlComposer::getChangedCompositionTypes(
+        Display display, std::vector<Layer>* outLayers,
+        std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
+    mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
+    return Error::NONE;
+}
+
+Error HidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
+    Error error = kDefaultError;
+
+    if (mClient_2_3) {
+        mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
+            error = tmpError;
+            if (error != Error::NONE) {
+                return;
+            }
+
+            *outModes = tmpModes;
+        });
+    } else if (mClient_2_2) {
+        mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
+            error = tmpError;
+            if (error != Error::NONE) {
+                return;
+            }
+
+            for (types::V1_1::ColorMode colorMode : tmpModes) {
+                outModes->push_back(static_cast<ColorMode>(colorMode));
+            }
+        });
+    } else {
+        mClient->getColorModes(display, [&](const auto& tmpError, const auto& tmpModes) {
+            error = tmpError;
+            if (error != Error::NONE) {
+                return;
+            }
+            for (types::V1_0::ColorMode colorMode : tmpModes) {
+                outModes->push_back(static_cast<ColorMode>(colorMode));
+            }
+        });
+    }
+
+    return error;
+}
+
+Error HidlComposer::getDisplayAttribute(Display display, Config config,
+                                        IComposerClient::Attribute attribute, int32_t* outValue) {
+    Error error = kDefaultError;
+    if (mClient_2_4) {
+        mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
+                                             [&](const auto& tmpError, const auto& tmpValue) {
+                                                 error = static_cast<Error>(tmpError);
+                                                 if (error != Error::NONE) {
+                                                     return;
+                                                 }
+
+                                                 *outValue = tmpValue;
+                                             });
+    } else {
+        mClient->getDisplayAttribute(display, config,
+                                     static_cast<V2_1::IComposerClient::Attribute>(attribute),
+                                     [&](const auto& tmpError, const auto& tmpValue) {
+                                         error = tmpError;
+                                         if (error != Error::NONE) {
+                                             return;
+                                         }
+
+                                         *outValue = tmpValue;
+                                     });
+    }
+
+    return error;
+}
+
+Error HidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
+    Error error = kDefaultError;
+    mClient->getDisplayConfigs(display, [&](const auto& tmpError, const auto& tmpConfigs) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outConfigs = tmpConfigs;
+    });
+
+    return error;
+}
+
+Error HidlComposer::getDisplayName(Display display, std::string* outName) {
+    Error error = kDefaultError;
+    mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outName = tmpName.c_str();
+    });
+
+    return error;
+}
+
+Error HidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+                                       std::vector<Layer>* outLayers,
+                                       std::vector<uint32_t>* outLayerRequestMasks) {
+    mReader.takeDisplayRequests(display, outDisplayRequestMask, outLayers, outLayerRequestMasks);
+    return Error::NONE;
+}
+
+Error HidlComposer::getDozeSupport(Display display, bool* outSupport) {
+    Error error = kDefaultError;
+    mClient->getDozeSupport(display, [&](const auto& tmpError, const auto& tmpSupport) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outSupport = tmpSupport;
+    });
+
+    return error;
+}
+
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+                                       float* outMaxLuminance, float* outMaxAverageLuminance,
+                                       float* outMinLuminance) {
+    Error error = kDefaultError;
+    if (mClient_2_3) {
+        mClient_2_3->getHdrCapabilities_2_3(display,
+                                            [&](const auto& tmpError, const auto& tmpTypes,
+                                                const auto& tmpMaxLuminance,
+                                                const auto& tmpMaxAverageLuminance,
+                                                const auto& tmpMinLuminance) {
+                                                error = tmpError;
+                                                if (error != Error::NONE) {
+                                                    return;
+                                                }
+
+                                                *outTypes = tmpTypes;
+                                                *outMaxLuminance = tmpMaxLuminance;
+                                                *outMaxAverageLuminance = tmpMaxAverageLuminance;
+                                                *outMinLuminance = tmpMinLuminance;
+                                            });
+    } else {
+        mClient->getHdrCapabilities(display,
+                                    [&](const auto& tmpError, const auto& tmpTypes,
+                                        const auto& tmpMaxLuminance,
+                                        const auto& tmpMaxAverageLuminance,
+                                        const auto& tmpMinLuminance) {
+                                        error = tmpError;
+                                        if (error != Error::NONE) {
+                                            return;
+                                        }
+
+                                        outTypes->clear();
+                                        for (auto type : tmpTypes) {
+                                            outTypes->push_back(static_cast<Hdr>(type));
+                                        }
+
+                                        *outMaxLuminance = tmpMaxLuminance;
+                                        *outMaxAverageLuminance = tmpMaxAverageLuminance;
+                                        *outMinLuminance = tmpMinLuminance;
+                                    });
+    }
+
+    return error;
+}
+
+Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
+                                     std::vector<int>* outReleaseFences) {
+    mReader.takeReleaseFences(display, outLayers, outReleaseFences);
+    return Error::NONE;
+}
+
+Error HidlComposer::presentDisplay(Display display, int* outPresentFence) {
+    ATRACE_NAME("HwcPresentDisplay");
+    mWriter.selectDisplay(display);
+    mWriter.presentDisplay();
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.takePresentFence(display, outPresentFence);
+
+    return Error::NONE;
+}
+
+Error HidlComposer::setActiveConfig(Display display, Config config) {
+    auto ret = mClient->setActiveConfig(display, config);
+    return unwrapRet(ret);
+}
+
+Error HidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+                                    int acquireFence, Dataspace dataspace,
+                                    const std::vector<IComposerClient::Rect>& damage) {
+    mWriter.selectDisplay(display);
+
+    const native_handle_t* handle = nullptr;
+    if (target.get()) {
+        handle = target->getNativeBuffer()->handle;
+    }
+
+    mWriter.setClientTarget(slot, handle, acquireFence, dataspace, damage);
+    return Error::NONE;
+}
+
+Error HidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
+    hardware::Return<Error> ret(kDefaultError);
+    if (mClient_2_3) {
+        ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
+    } else if (mClient_2_2) {
+        ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
+                                            renderIntent);
+    } else {
+        ret = mClient->setColorMode(display, static_cast<types::V1_0::ColorMode>(mode));
+    }
+    return unwrapRet(ret);
+}
+
+Error HidlComposer::setColorTransform(Display display, const float* matrix) {
+    mWriter.selectDisplay(display);
+    const bool isIdentity = (mat4(matrix) == mat4());
+    mWriter.setColorTransform(matrix,
+                              isIdentity ? ColorTransform::IDENTITY
+                                         : ColorTransform::ARBITRARY_MATRIX);
+    return Error::NONE;
+}
+
+Error HidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
+                                    int releaseFence) {
+    mWriter.selectDisplay(display);
+    mWriter.setOutputBuffer(0, buffer, dup(releaseFence));
+    return Error::NONE;
+}
+
+Error HidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+    Return<Error> ret(Error::UNSUPPORTED);
+    if (mClient_2_2) {
+        ret = mClient_2_2->setPowerMode_2_2(display, mode);
+    } else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
+        ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
+    }
+
+    return unwrapRet(ret);
+}
+
+Error HidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
+    auto ret = mClient->setVsyncEnabled(display, enabled);
+    return unwrapRet(ret);
+}
+
+Error HidlComposer::setClientTargetSlotCount(Display display) {
+    const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
+    auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
+    return unwrapRet(ret);
+}
+
+Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
+                                    uint32_t* outNumTypes, uint32_t* outNumRequests) {
+    ATRACE_NAME("HwcValidateDisplay");
+    mWriter.selectDisplay(display);
+    mWriter.validateDisplay();
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.hasChanges(display, outNumTypes, outNumRequests);
+
+    return Error::NONE;
+}
+
+Error HidlComposer::presentOrValidateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
+                                             uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                             int* outPresentFence, uint32_t* state) {
+    ATRACE_NAME("HwcPresentOrValidateDisplay");
+    mWriter.selectDisplay(display);
+    mWriter.presentOrvalidateDisplay();
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.takePresentOrValidateStage(display, state);
+
+    if (*state == 1) { // Present succeeded
+        mReader.takePresentFence(display, outPresentFence);
+    }
+
+    if (*state == 0) { // Validate succeeded.
+        mReader.hasChanges(display, outNumTypes, outNumRequests);
+    }
+
+    return Error::NONE;
+}
+
+Error HidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerCursorPosition(x, y);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
+                                   const sp<GraphicBuffer>& buffer, int acquireFence) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+
+    const native_handle_t* handle = nullptr;
+    if (buffer.get()) {
+        handle = buffer->getNativeBuffer()->handle;
+    }
+
+    mWriter.setLayerBuffer(slot, handle, acquireFence);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
+                                          const std::vector<IComposerClient::Rect>& damage) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerSurfaceDamage(damage);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerBlendMode(Display display, Layer layer,
+                                      IComposerClient::BlendMode mode) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerBlendMode(mode);
+    return Error::NONE;
+}
+
+static IComposerClient::Color to_hidl_type(
+        aidl::android::hardware::graphics::composer3::Color color) {
+    const auto floatColorToUint8Clamped = [](float val) -> uint8_t {
+        const auto intVal = static_cast<uint64_t>(std::round(255.0f * val));
+        const auto minVal = static_cast<uint64_t>(0);
+        const auto maxVal = static_cast<uint64_t>(255);
+        return std::clamp(intVal, minVal, maxVal);
+    };
+
+    return IComposerClient::Color{
+            floatColorToUint8Clamped(color.r),
+            floatColorToUint8Clamped(color.g),
+            floatColorToUint8Clamped(color.b),
+            floatColorToUint8Clamped(color.a),
+    };
+}
+
+Error HidlComposer::setLayerColor(
+        Display display, Layer layer,
+        const aidl::android::hardware::graphics::composer3::Color& color) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerColor(to_hidl_type(color));
+    return Error::NONE;
+}
+
+static IComposerClient::Composition to_hidl_type(
+        aidl::android::hardware::graphics::composer3::Composition type) {
+    LOG_ALWAYS_FATAL_IF(static_cast<int32_t>(type) >
+                                static_cast<int32_t>(IComposerClient::Composition::SIDEBAND),
+                        "Trying to use %s, which is not supported by HidlComposer!",
+                        android::to_string(type).c_str());
+
+    return static_cast<IComposerClient::Composition>(type);
+}
+
+Error HidlComposer::setLayerCompositionType(
+        Display display, Layer layer,
+        aidl::android::hardware::graphics::composer3::Composition type) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerCompositionType(to_hidl_type(type));
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerDataspace(dataspace);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerDisplayFrame(Display display, Layer layer,
+                                         const IComposerClient::Rect& frame) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerDisplayFrame(frame);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerPlaneAlpha(alpha);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerSidebandStream(Display display, Layer layer,
+                                           const native_handle_t* stream) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerSidebandStream(stream);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerSourceCrop(Display display, Layer layer,
+                                       const IComposerClient::FRect& crop) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerSourceCrop(crop);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerTransform(transform);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerVisibleRegion(Display display, Layer layer,
+                                          const std::vector<IComposerClient::Rect>& visible) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerVisibleRegion(visible);
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerZOrder(z);
+    return Error::NONE;
+}
+
+Error HidlComposer::execute() {
+    // prepare input command queue
+    bool queueChanged = false;
+    uint32_t commandLength = 0;
+    hidl_vec<hidl_handle> commandHandles;
+    if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
+        mWriter.reset();
+        return Error::NO_RESOURCES;
+    }
+
+    // set up new input command queue if necessary
+    if (queueChanged) {
+        auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
+        auto error = unwrapRet(ret);
+        if (error != Error::NONE) {
+            mWriter.reset();
+            return error;
+        }
+    }
+
+    if (commandLength == 0) {
+        mWriter.reset();
+        return Error::NONE;
+    }
+
+    Error error = kDefaultError;
+    hardware::Return<void> ret;
+    auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
+                             const auto& tmpOutLength, const auto& tmpOutHandles) {
+        error = tmpError;
+
+        // set up new output command queue if necessary
+        if (error == Error::NONE && tmpOutChanged) {
+            error = kDefaultError;
+            mClient->getOutputCommandQueue([&](const auto& tmpError, const auto& tmpDescriptor) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                mReader.setMQDescriptor(tmpDescriptor);
+            });
+        }
+
+        if (error != Error::NONE) {
+            return;
+        }
+
+        if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
+            error = mReader.parse();
+            mReader.reset();
+        } else {
+            error = Error::NO_RESOURCES;
+        }
+    };
+    if (mClient_2_2) {
+        ret = mClient_2_2->executeCommands_2_2(commandLength, commandHandles, hidl_callback);
+    } else {
+        ret = mClient->executeCommands(commandLength, commandHandles, hidl_callback);
+    }
+    // executeCommands can fail because of out-of-fd and we do not want to
+    // abort() in that case
+    if (!ret.isOk()) {
+        ALOGE("executeCommands failed because of %s", ret.description().c_str());
+    }
+
+    if (error == Error::NONE) {
+        std::vector<CommandReader::CommandError> commandErrors = mReader.takeErrors();
+
+        for (const auto& cmdErr : commandErrors) {
+            auto command =
+                    static_cast<IComposerClient::Command>(mWriter.getCommand(cmdErr.location));
+
+            if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
+                command == IComposerClient::Command::PRESENT_DISPLAY ||
+                command == IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY) {
+                error = cmdErr.error;
+            } else {
+                ALOGW("command 0x%x generated error %d", command, cmdErr.error);
+            }
+        }
+    }
+
+    mWriter.reset();
+
+    return error;
+}
+
+// Composer HAL 2.2
+
+Error HidlComposer::setLayerPerFrameMetadata(
+        Display display, Layer layer,
+        const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
+    if (!mClient_2_2) {
+        return Error::UNSUPPORTED;
+    }
+
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerPerFrameMetadata(perFrameMetadatas);
+    return Error::NONE;
+}
+
+std::vector<IComposerClient::PerFrameMetadataKey> HidlComposer::getPerFrameMetadataKeys(
+        Display display) {
+    std::vector<IComposerClient::PerFrameMetadataKey> keys;
+    if (!mClient_2_2) {
+        return keys;
+    }
+
+    Error error = kDefaultError;
+    if (mClient_2_3) {
+        mClient_2_3->getPerFrameMetadataKeys_2_3(display,
+                                                 [&](const auto& tmpError, const auto& tmpKeys) {
+                                                     error = tmpError;
+                                                     if (error != Error::NONE) {
+                                                         ALOGW("getPerFrameMetadataKeys failed "
+                                                               "with %d",
+                                                               tmpError);
+                                                         return;
+                                                     }
+                                                     keys = tmpKeys;
+                                                 });
+    } else {
+        mClient_2_2
+                ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
+                    error = tmpError;
+                    if (error != Error::NONE) {
+                        ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
+                        return;
+                    }
+
+                    keys.clear();
+                    for (auto key : tmpKeys) {
+                        keys.push_back(static_cast<IComposerClient::PerFrameMetadataKey>(key));
+                    }
+                });
+    }
+
+    return keys;
+}
+
+Error HidlComposer::getRenderIntents(Display display, ColorMode colorMode,
+                                     std::vector<RenderIntent>* outRenderIntents) {
+    if (!mClient_2_2) {
+        outRenderIntents->push_back(RenderIntent::COLORIMETRIC);
+        return Error::NONE;
+    }
+
+    Error error = kDefaultError;
+
+    auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outRenderIntents = tmpKeys;
+    };
+
+    if (mClient_2_3) {
+        mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
+    } else {
+        mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
+                                      getRenderIntentsLambda);
+    }
+
+    return error;
+}
+
+Error HidlComposer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) {
+    if (!mClient_2_2) {
+        *outMatrix = mat4();
+        return Error::NONE;
+    }
+
+    Error error = kDefaultError;
+    mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
+                                              [&](const auto& tmpError, const auto& tmpMatrix) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
+                                                  *outMatrix = mat4(tmpMatrix.data());
+                                              });
+
+    return error;
+}
+
+// Composer HAL 2.3
+
+Error HidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                                 std::vector<uint8_t>* outData) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayIdentificationData(display,
+                                              [&](const auto& tmpError, const auto& tmpPort,
+                                                  const auto& tmpData) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
+
+                                                  *outPort = tmpPort;
+                                                  *outData = tmpData;
+                                              });
+
+    return error;
+}
+
+Error HidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerColorTransform(matrix);
+    return Error::NONE;
+}
+
+Error HidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                          Dataspace* outDataspace,
+                                                          uint8_t* outComponentMask) {
+    if (!outFormat || !outDataspace || !outComponentMask) {
+        return Error::BAD_PARAMETER;
+    }
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayedContentSamplingAttributes(display,
+                                                       [&](const auto tmpError,
+                                                           const auto& tmpFormat,
+                                                           const auto& tmpDataspace,
+                                                           const auto& tmpComponentMask) {
+                                                           error = tmpError;
+                                                           if (error == Error::NONE) {
+                                                               *outFormat = tmpFormat;
+                                                               *outDataspace = tmpDataspace;
+                                                               *outComponentMask =
+                                                                       static_cast<uint8_t>(
+                                                                               tmpComponentMask);
+                                                           }
+                                                       });
+    return error;
+}
+
+Error HidlComposer::setDisplayContentSamplingEnabled(Display display, bool enabled,
+                                                     uint8_t componentMask, uint64_t maxFrames) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
+                          : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
+    return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+                                                           maxFrames);
+}
+
+Error HidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
+                                              uint64_t timestamp, DisplayedFrameStats* outStats) {
+    if (!outStats) {
+        return Error::BAD_PARAMETER;
+    }
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
+                                           [&](const auto tmpError, auto tmpNumFrames,
+                                               const auto& tmpSamples0, const auto& tmpSamples1,
+                                               const auto& tmpSamples2, const auto& tmpSamples3) {
+                                               error = tmpError;
+                                               if (error == Error::NONE) {
+                                                   outStats->numFrames = tmpNumFrames;
+                                                   outStats->component_0_sample = tmpSamples0;
+                                                   outStats->component_1_sample = tmpSamples1;
+                                                   outStats->component_2_sample = tmpSamples2;
+                                                   outStats->component_3_sample = tmpSamples3;
+                                               }
+                                           });
+    return error;
+}
+
+Error HidlComposer::setLayerPerFrameMetadataBlobs(
+        Display display, Layer layer,
+        const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerPerFrameMetadataBlobs(metadata);
+    return Error::NONE;
+}
+
+Error HidlComposer::setDisplayBrightness(Display display, float brightness,
+                                         const DisplayBrightnessOptions&) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    return mClient_2_3->setDisplayBrightness(display, brightness);
+}
+
+// Composer HAL 2.4
+
+namespace {
+template <typename T>
+void copyCapabilities(const T& tmpCaps, std::vector<DisplayCapability>* outCapabilities) {
+    outCapabilities->resize(tmpCaps.size());
+    std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
+                   [](auto cap) { return static_cast<DisplayCapability>(cap); });
+}
+} // anonymous namespace
+
+Error HidlComposer::getDisplayCapabilities(Display display,
+                                           std::vector<DisplayCapability>* outCapabilities) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    V2_4::Error error = kDefaultError_2_4;
+    if (mClient_2_4) {
+        mClient_2_4->getDisplayCapabilities_2_4(display,
+                                                [&](const auto& tmpError, const auto& tmpCaps) {
+                                                    error = tmpError;
+                                                    if (error != V2_4::Error::NONE) {
+                                                        return;
+                                                    }
+                                                    copyCapabilities(tmpCaps, outCapabilities);
+                                                });
+    } else {
+        mClient_2_3
+                ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
+                    error = static_cast<V2_4::Error>(tmpError);
+                    if (error != V2_4::Error::NONE) {
+                        return;
+                    }
+
+                    copyCapabilities(tmpCaps, outCapabilities);
+                });
+    }
+
+    return static_cast<Error>(error);
+}
+
+V2_4::Error HidlComposer::getDisplayConnectionType(
+        Display display, IComposerClient::DisplayConnectionType* outType) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
+        error = tmpError;
+        if (error != V2_4::Error::NONE) {
+            return;
+        }
+
+        *outType = tmpType;
+    });
+
+    return error;
+}
+
+V2_4::Error HidlComposer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getDisplayVsyncPeriod(display,
+                                       [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
+                                           error = tmpError;
+                                           if (error != Error::NONE) {
+                                               return;
+                                           }
+
+                                           *outVsyncPeriod = tmpVsyncPeriod;
+                                       });
+
+    return error;
+}
+
+V2_4::Error HidlComposer::setActiveConfigWithConstraints(
+        Display display, Config config,
+        const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+        VsyncPeriodChangeTimeline* outTimeline) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
+                                                [&](const auto& tmpError, const auto& tmpTimeline) {
+                                                    error = tmpError;
+                                                    if (error != Error::NONE) {
+                                                        return;
+                                                    }
+
+                                                    *outTimeline = tmpTimeline;
+                                                });
+
+    return error;
+}
+
+V2_4::Error HidlComposer::setAutoLowLatencyMode(Display display, bool on) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    return mClient_2_4->setAutoLowLatencyMode(display, on);
+}
+
+V2_4::Error HidlComposer::getSupportedContentTypes(
+        Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getSupportedContentTypes(displayId,
+                                          [&](const auto& tmpError,
+                                              const auto& tmpSupportedContentTypes) {
+                                              error = tmpError;
+                                              if (error != Error::NONE) {
+                                                  return;
+                                              }
+
+                                              *outSupportedContentTypes = tmpSupportedContentTypes;
+                                          });
+    return error;
+}
+
+V2_4::Error HidlComposer::setContentType(Display display,
+                                         IComposerClient::ContentType contentType) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    return mClient_2_4->setContentType(display, contentType);
+}
+
+V2_4::Error HidlComposer::setLayerGenericMetadata(Display display, Layer layer,
+                                                  const std::string& key, bool mandatory,
+                                                  const std::vector<uint8_t>& value) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerGenericMetadata(key, mandatory, value);
+    return Error::NONE;
+}
+
+V2_4::Error HidlComposer::getLayerGenericMetadataKeys(
+        std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getLayerGenericMetadataKeys([&](const auto& tmpError, const auto& tmpKeys) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outKeys = tmpKeys;
+    });
+    return error;
+}
+
+Error HidlComposer::getClientTargetProperty(
+        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+        float* outWhitePointNits) {
+    mReader.takeClientTargetProperty(display, outClientTargetProperty);
+    *outWhitePointNits = -1.f;
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerWhitePointNits(Display, Layer, float) {
+    return Error::NONE;
+}
+
+Error HidlComposer::setLayerBlockingRegion(Display, Layer,
+                                           const std::vector<IComposerClient::Rect>&) {
+    return Error::NONE;
+}
+
+CommandReader::~CommandReader() {
+    resetData();
+}
+
+Error CommandReader::parse() {
+    resetData();
+
+    IComposerClient::Command command;
+    uint16_t length = 0;
+
+    while (!isEmpty()) {
+        if (!beginCommand(&command, &length)) {
+            break;
+        }
+
+        bool parsed = false;
+        switch (command) {
+            case IComposerClient::Command::SELECT_DISPLAY:
+                parsed = parseSelectDisplay(length);
+                break;
+            case IComposerClient::Command::SET_ERROR:
+                parsed = parseSetError(length);
+                break;
+            case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
+                parsed = parseSetChangedCompositionTypes(length);
+                break;
+            case IComposerClient::Command::SET_DISPLAY_REQUESTS:
+                parsed = parseSetDisplayRequests(length);
+                break;
+            case IComposerClient::Command::SET_PRESENT_FENCE:
+                parsed = parseSetPresentFence(length);
+                break;
+            case IComposerClient::Command::SET_RELEASE_FENCES:
+                parsed = parseSetReleaseFences(length);
+                break;
+            case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
+                parsed = parseSetPresentOrValidateDisplayResult(length);
+                break;
+            case IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY:
+                parsed = parseSetClientTargetProperty(length);
+                break;
+            default:
+                parsed = false;
+                break;
+        }
+
+        endCommand();
+
+        if (!parsed) {
+            ALOGE("failed to parse command 0x%x length %" PRIu16, command, length);
+            break;
+        }
+    }
+
+    return isEmpty() ? Error::NONE : Error::NO_RESOURCES;
+}
+
+bool CommandReader::parseSelectDisplay(uint16_t length) {
+    if (length != CommandWriterBase::kSelectDisplayLength) {
+        return false;
+    }
+
+    mCurrentReturnData = &mReturnData[read64()];
+
+    return true;
+}
+
+bool CommandReader::parseSetError(uint16_t length) {
+    if (length != CommandWriterBase::kSetErrorLength) {
+        return false;
+    }
+
+    auto location = read();
+    auto error = static_cast<Error>(readSigned());
+
+    mErrors.emplace_back(CommandError{location, error});
+
+    return true;
+}
+
+bool CommandReader::parseSetChangedCompositionTypes(uint16_t length) {
+    // (layer id, composition type) pairs
+    if (length % 3 != 0 || !mCurrentReturnData) {
+        return false;
+    }
+
+    uint32_t count = length / 3;
+    mCurrentReturnData->changedLayers.reserve(count);
+    mCurrentReturnData->compositionTypes.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto type = static_cast<aidl::android::hardware::graphics::composer3::Composition>(
+                readSigned());
+
+        mCurrentReturnData->changedLayers.push_back(layer);
+        mCurrentReturnData->compositionTypes.push_back(type);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetDisplayRequests(uint16_t length) {
+    // display requests followed by (layer id, layer requests) pairs
+    if (length % 3 != 1 || !mCurrentReturnData) {
+        return false;
+    }
+
+    mCurrentReturnData->displayRequests = read();
+
+    uint32_t count = (length - 1) / 3;
+    mCurrentReturnData->requestedLayers.reserve(count);
+    mCurrentReturnData->requestMasks.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto layerRequestMask = read();
+
+        mCurrentReturnData->requestedLayers.push_back(layer);
+        mCurrentReturnData->requestMasks.push_back(layerRequestMask);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetPresentFence(uint16_t length) {
+    if (length != CommandWriterBase::kSetPresentFenceLength || !mCurrentReturnData) {
+        return false;
+    }
+
+    if (mCurrentReturnData->presentFence >= 0) {
+        close(mCurrentReturnData->presentFence);
+    }
+    mCurrentReturnData->presentFence = readFence();
+
+    return true;
+}
+
+bool CommandReader::parseSetReleaseFences(uint16_t length) {
+    // (layer id, release fence index) pairs
+    if (length % 3 != 0 || !mCurrentReturnData) {
+        return false;
+    }
+
+    uint32_t count = length / 3;
+    mCurrentReturnData->releasedLayers.reserve(count);
+    mCurrentReturnData->releaseFences.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto fence = readFence();
+
+        mCurrentReturnData->releasedLayers.push_back(layer);
+        mCurrentReturnData->releaseFences.push_back(fence);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length) {
+    if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
+        return false;
+    }
+    mCurrentReturnData->presentOrValidateState = read();
+    return true;
+}
+
+bool CommandReader::parseSetClientTargetProperty(uint16_t length) {
+    if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) {
+        return false;
+    }
+    mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned());
+    mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned());
+    return true;
+}
+
+void CommandReader::resetData() {
+    mErrors.clear();
+
+    for (auto& data : mReturnData) {
+        if (data.second.presentFence >= 0) {
+            close(data.second.presentFence);
+        }
+        for (auto fence : data.second.releaseFences) {
+            if (fence >= 0) {
+                close(fence);
+            }
+        }
+    }
+
+    mReturnData.clear();
+    mCurrentReturnData = nullptr;
+}
+
+std::vector<CommandReader::CommandError> CommandReader::takeErrors() {
+    return std::move(mErrors);
+}
+
+bool CommandReader::hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
+                               uint32_t* outNumLayerRequestMasks) const {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outNumChangedCompositionTypes = 0;
+        *outNumLayerRequestMasks = 0;
+        return false;
+    }
+
+    const ReturnData& data = found->second;
+
+    *outNumChangedCompositionTypes = data.compositionTypes.size();
+    *outNumLayerRequestMasks = data.requestMasks.size();
+
+    return !(data.compositionTypes.empty() && data.requestMasks.empty());
+}
+
+void CommandReader::takeChangedCompositionTypes(
+        Display display, std::vector<Layer>* outLayers,
+        std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        outLayers->clear();
+        outTypes->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outLayers = std::move(data.changedLayers);
+    *outTypes = std::move(data.compositionTypes);
+}
+
+void CommandReader::takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+                                        std::vector<Layer>* outLayers,
+                                        std::vector<uint32_t>* outLayerRequestMasks) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outDisplayRequestMask = 0;
+        outLayers->clear();
+        outLayerRequestMasks->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outDisplayRequestMask = data.displayRequests;
+    *outLayers = std::move(data.requestedLayers);
+    *outLayerRequestMasks = std::move(data.requestMasks);
+}
+
+void CommandReader::takeReleaseFences(Display display, std::vector<Layer>* outLayers,
+                                      std::vector<int>* outReleaseFences) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        outLayers->clear();
+        outReleaseFences->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outLayers = std::move(data.releasedLayers);
+    *outReleaseFences = std::move(data.releaseFences);
+}
+
+void CommandReader::takePresentFence(Display display, int* outPresentFence) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outPresentFence = -1;
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outPresentFence = data.presentFence;
+    data.presentFence = -1;
+}
+
+void CommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *state = -1;
+        return;
+    }
+    ReturnData& data = found->second;
+    *state = data.presentOrValidateState;
+}
+
+void CommandReader::takeClientTargetProperty(
+        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+    auto found = mReturnData.find(display);
+
+    // If not found, return the default values.
+    if (found == mReturnData.end()) {
+        outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
+        outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+        return;
+    }
+
+    ReturnData& data = found->second;
+    *outClientTargetProperty = data.clientTargetProperty;
+}
+
+} // namespace Hwc2
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
new file mode 100644
index 0000000..8282d8a
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ComposerHal.h"
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
+#include <gui/BufferQueue.h>
+#include <gui/HdrMetadata.h>
+#include <math/mat4.h>
+#include <ui/DisplayedFrameStats.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/StrongPointer.h>
+
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+
+namespace android::Hwc2 {
+
+namespace types = hardware::graphics::common;
+
+namespace V2_1 = hardware::graphics::composer::V2_1;
+namespace V2_2 = hardware::graphics::composer::V2_2;
+namespace V2_3 = hardware::graphics::composer::V2_3;
+namespace V2_4 = hardware::graphics::composer::V2_4;
+
+using types::V1_0::ColorTransform;
+using types::V1_0::Transform;
+using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
+using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
+
+using V2_1::Config;
+using V2_1::Display;
+using V2_1::Error;
+using V2_1::Layer;
+using V2_4::CommandReaderBase;
+using V2_4::CommandWriterBase;
+using V2_4::IComposer;
+using V2_4::IComposerCallback;
+using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
+using PerFrameMetadata = IComposerClient::PerFrameMetadata;
+using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
+using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
+
+class CommandReader : public CommandReaderBase {
+public:
+    ~CommandReader();
+
+    // Parse and execute commands from the command queue.  The commands are
+    // actually return values from the server and will be saved in ReturnData.
+    Error parse();
+
+    // Get and clear saved errors.
+    struct CommandError {
+        uint32_t location;
+        Error error;
+    };
+    std::vector<CommandError> takeErrors();
+
+    bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
+                    uint32_t* outNumLayerRequestMasks) const;
+
+    // Get and clear saved changed composition types.
+    void takeChangedCompositionTypes(
+            Display display, std::vector<Layer>* outLayers,
+            std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes);
+
+    // Get and clear saved display requests.
+    void takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+                             std::vector<Layer>* outLayers,
+                             std::vector<uint32_t>* outLayerRequestMasks);
+
+    // Get and clear saved release fences.
+    void takeReleaseFences(Display display, std::vector<Layer>* outLayers,
+                           std::vector<int>* outReleaseFences);
+
+    // Get and clear saved present fence.
+    void takePresentFence(Display display, int* outPresentFence);
+
+    // Get what stage succeeded during PresentOrValidate: Present or Validate
+    void takePresentOrValidateStage(Display display, uint32_t* state);
+
+    // Get the client target properties requested by hardware composer.
+    void takeClientTargetProperty(Display display,
+                                  IComposerClient::ClientTargetProperty* outClientTargetProperty);
+
+private:
+    void resetData();
+
+    bool parseSelectDisplay(uint16_t length);
+    bool parseSetError(uint16_t length);
+    bool parseSetChangedCompositionTypes(uint16_t length);
+    bool parseSetDisplayRequests(uint16_t length);
+    bool parseSetPresentFence(uint16_t length);
+    bool parseSetReleaseFences(uint16_t length);
+    bool parseSetPresentOrValidateDisplayResult(uint16_t length);
+    bool parseSetClientTargetProperty(uint16_t length);
+
+    struct ReturnData {
+        uint32_t displayRequests = 0;
+
+        std::vector<Layer> changedLayers;
+        std::vector<aidl::android::hardware::graphics::composer3::Composition> compositionTypes;
+
+        std::vector<Layer> requestedLayers;
+        std::vector<uint32_t> requestMasks;
+
+        int presentFence = -1;
+
+        std::vector<Layer> releasedLayers;
+        std::vector<int> releaseFences;
+
+        uint32_t presentOrValidateState;
+
+        // Composer 2.4 implementation can return a client target property
+        // structure to indicate the client target properties that hardware
+        // composer requests. The composer client must change the client target
+        // properties to match this request.
+        IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
+                                                                   Dataspace::UNKNOWN};
+    };
+
+    std::vector<CommandError> mErrors;
+    std::unordered_map<Display, ReturnData> mReturnData;
+
+    // When SELECT_DISPLAY is parsed, this is updated to point to the
+    // display's return data in mReturnData.  We use it to avoid repeated
+    // map lookups.
+    ReturnData* mCurrentReturnData;
+};
+
+// Composer is a wrapper to IComposer, a proxy to server-side composer.
+class HidlComposer final : public Composer {
+public:
+    explicit HidlComposer(const std::string& serviceName);
+    ~HidlComposer() override;
+
+    bool isSupported(OptionalFeature) const;
+
+    std::vector<IComposer::Capability> getCapabilities() override;
+    std::string dumpDebugInfo() override;
+
+    void registerCallback(const sp<IComposerCallback>& callback) override;
+
+    // Reset all pending commands in the command buffer. Useful if you want to
+    // skip a frame but have already queued some commands.
+    void resetCommands() override;
+
+    // Explicitly flush all pending commands in the command buffer.
+    Error executeCommands() override;
+
+    uint32_t getMaxVirtualDisplayCount() override;
+    Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+                               Display* outDisplay) override;
+    Error destroyVirtualDisplay(Display display) override;
+
+    Error acceptDisplayChanges(Display display) override;
+
+    Error createLayer(Display display, Layer* outLayer) override;
+    Error destroyLayer(Display display, Layer layer) override;
+
+    Error getActiveConfig(Display display, Config* outConfig) override;
+    Error getChangedCompositionTypes(
+            Display display, std::vector<Layer>* outLayers,
+            std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+            override;
+    Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
+    Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+                              int32_t* outValue) override;
+    Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+    Error getDisplayName(Display display, std::string* outName) override;
+
+    Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+                             std::vector<Layer>* outLayers,
+                             std::vector<uint32_t>* outLayerRequestMasks) override;
+
+    Error getDozeSupport(Display display, bool* outSupport) override;
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+                             float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+    Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
+                           std::vector<int>* outReleaseFences) override;
+
+    Error presentDisplay(Display display, int* outPresentFence) override;
+
+    Error setActiveConfig(Display display, Config config) override;
+
+    /*
+     * The composer caches client targets internally.  When target is nullptr,
+     * the composer uses slot to look up the client target from its cache.
+     * When target is not nullptr, the cache is updated with the new target.
+     */
+    Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+                          int acquireFence, Dataspace dataspace,
+                          const std::vector<IComposerClient::Rect>& damage) override;
+    Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
+    Error setColorTransform(Display display, const float* matrix) override;
+    Error setOutputBuffer(Display display, const native_handle_t* buffer,
+                          int releaseFence) override;
+    Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+    Error setClientTargetSlotCount(Display display) override;
+
+    Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+                          uint32_t* outNumRequests) override;
+
+    Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+                                   uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                   int* outPresentFence, uint32_t* state) override;
+
+    Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+    /* see setClientTarget for the purpose of slot */
+    Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
+                         const sp<GraphicBuffer>& buffer, int acquireFence) override;
+    Error setLayerSurfaceDamage(Display display, Layer layer,
+                                const std::vector<IComposerClient::Rect>& damage) override;
+    Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
+    Error setLayerColor(Display display, Layer layer,
+                        const aidl::android::hardware::graphics::composer3::Color& color) override;
+    Error setLayerCompositionType(
+            Display display, Layer layer,
+            aidl::android::hardware::graphics::composer3::Composition type) override;
+    Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
+    Error setLayerDisplayFrame(Display display, Layer layer,
+                               const IComposerClient::Rect& frame) override;
+    Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+    Error setLayerSidebandStream(Display display, Layer layer,
+                                 const native_handle_t* stream) override;
+    Error setLayerSourceCrop(Display display, Layer layer,
+                             const IComposerClient::FRect& crop) override;
+    Error setLayerTransform(Display display, Layer layer, Transform transform) override;
+    Error setLayerVisibleRegion(Display display, Layer layer,
+                                const std::vector<IComposerClient::Rect>& visible) override;
+    Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+    // Composer HAL 2.2
+    Error setLayerPerFrameMetadata(
+            Display display, Layer layer,
+            const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
+    std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+            Display display) override;
+    Error getRenderIntents(Display display, ColorMode colorMode,
+                           std::vector<RenderIntent>* outRenderIntents) override;
+    Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
+
+    // Composer HAL 2.3
+    Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                       std::vector<uint8_t>* outData) override;
+    Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+    Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                Dataspace* outDataspace,
+                                                uint8_t* outComponentMask) override;
+    Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
+                                           uint64_t maxFrames) override;
+    Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                    DisplayedFrameStats* outStats) override;
+    Error setLayerPerFrameMetadataBlobs(
+            Display display, Layer layer,
+            const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
+    Error setDisplayBrightness(Display display, float brightness,
+                               const DisplayBrightnessOptions& options) override;
+
+    // Composer HAL 2.4
+    Error getDisplayCapabilities(
+            Display display,
+            std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*
+                    outCapabilities) override;
+    V2_4::Error getDisplayConnectionType(Display display,
+                                         IComposerClient::DisplayConnectionType* outType) override;
+    V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+    V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) override;
+    V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+    V2_4::Error getSupportedContentTypes(
+            Display displayId,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+    V2_4::Error setContentType(Display displayId,
+                               IComposerClient::ContentType contentType) override;
+    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                        bool mandatory, const std::vector<uint8_t>& value) override;
+    V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+    Error getClientTargetProperty(Display display,
+                                  IComposerClient::ClientTargetProperty* outClientTargetProperty,
+                                  float* outWhitePointNits) override;
+
+    // AIDL Composer HAL
+    Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
+    Error setLayerBlockingRegion(Display display, Layer layer,
+                                 const std::vector<IComposerClient::Rect>& blocking) override;
+
+private:
+    class CommandWriter : public CommandWriterBase {
+    public:
+        explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
+        ~CommandWriter() override {}
+    };
+
+    // Many public functions above simply write a command into the command
+    // queue to batch the calls.  validateDisplay and presentDisplay will call
+    // this function to execute the command queue.
+    Error execute();
+
+    sp<V2_1::IComposer> mComposer;
+
+    sp<V2_1::IComposerClient> mClient;
+    sp<V2_2::IComposerClient> mClient_2_2;
+    sp<V2_3::IComposerClient> mClient_2_3;
+    sp<IComposerClient> mClient_2_4;
+
+    // 64KiB minus a small space for metadata such as read/write pointers
+    static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
+    // Max number of buffers that may be cached for a given layer
+    // We obtain this number by:
+    // 1. Tightly coupling this cache to the max size of BufferQueue
+    // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
+    static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
+    CommandWriter mWriter;
+    CommandReader mReader;
+};
+
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 1765caf..6f02843 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -19,7 +19,10 @@
 #undef LOG_TAG
 #define LOG_TAG "PowerAdvisor"
 
+#include <unistd.h>
 #include <cinttypes>
+#include <cstdint>
+#include <optional>
 
 #include <android-base/properties.h>
 #include <utils/Log.h>
@@ -27,6 +30,9 @@
 
 #include <android/hardware/power/1.3/IPower.h>
 #include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/WorkDuration.h>
+
 #include <binder/IServiceManager.h>
 
 #include "../SurfaceFlingerProperties.h"
@@ -47,10 +53,14 @@
 
 using android::hardware::power::Boost;
 using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
 using android::hardware::power::Mode;
-using base::GetIntProperty;
+using android::hardware::power::WorkDuration;
+
 using scheduler::OneShotTimer;
 
+class AidlPowerHalWrapper;
+
 PowerAdvisor::~PowerAdvisor() = default;
 
 namespace {
@@ -136,6 +146,74 @@
     }
 }
 
+// checks both if it supports and if it's enabled
+bool PowerAdvisor::usePowerHintSession() {
+    // uses cached value since the underlying support and flag are unlikely to change at runtime
+    return mPowerHintEnabled.value_or(false) && supportsPowerHintSession();
+}
+
+bool PowerAdvisor::supportsPowerHintSession() {
+    // cache to avoid needing lock every time
+    if (!mSupportsPowerHint.has_value()) {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        mSupportsPowerHint = halWrapper->supportsPowerHintSession();
+    }
+    return *mSupportsPowerHint;
+}
+
+bool PowerAdvisor::isPowerHintSessionRunning() {
+    return mPowerHintSessionRunning;
+}
+
+void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
+    if (!usePowerHintSession()) {
+        ALOGV("Power hint session target duration cannot be set, skipping");
+        return;
+    }
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper != nullptr) {
+            halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count());
+        }
+    }
+}
+
+void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) {
+    if (!mBootFinished || !usePowerHintSession()) {
+        ALOGV("Actual work duration power hint cannot be sent, skipping");
+        return;
+    }
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper != nullptr) {
+            halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos);
+        }
+    }
+}
+
+// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished
+void PowerAdvisor::enablePowerHint(bool enabled) {
+    mPowerHintEnabled = enabled;
+}
+
+bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) {
+    if (!usePowerHintSession()) {
+        ALOGI("Power hint session cannot be started, skipping");
+    }
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* halWrapper = getPowerHal();
+        if (halWrapper != nullptr && usePowerHintSession()) {
+            halWrapper->setPowerHintSessionThreadIds(threadIds);
+            mPowerHintSessionRunning = halWrapper->startPowerHintSession();
+        }
+    }
+    return mPowerHintSessionRunning;
+}
+
 class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
 public:
     HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -178,6 +256,26 @@
         return true;
     }
 
+    bool supportsPowerHintSession() override { return false; }
+
+    bool isPowerHintSessionRunning() override { return false; }
+
+    void restartPowerHintSession() override {}
+
+    void setPowerHintSessionThreadIds(const std::vector<int32_t>&) override {}
+
+    bool startPowerHintSession() override { return false; }
+
+    void setTargetWorkDuration(int64_t) override {}
+
+    void sendActualWorkDuration(int64_t, nsecs_t) override {}
+
+    bool shouldReconnectHAL() override { return false; }
+
+    std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; }
+
+    std::optional<int64_t> getTargetWorkDuration() override { return std::nullopt; }
+
 private:
     const sp<V1_3::IPower> mPowerHal = nullptr;
 };
@@ -195,9 +293,16 @@
         if (!ret.isOk()) {
             mHasDisplayUpdateImminent = false;
         }
+
+        mSupportsPowerHint = checkPowerHintSessionSupported();
     }
 
-    ~AidlPowerHalWrapper() override = default;
+    ~AidlPowerHalWrapper() override {
+        if (mPowerHintSession != nullptr) {
+            mPowerHintSession->close();
+            mPowerHintSession = nullptr;
+        }
+    };
 
     static std::unique_ptr<HalWrapper> connect() {
         // This only waits if the service is actually declared
@@ -232,12 +337,205 @@
         return ret.isOk();
     }
 
+    // only version 2+ of the aidl supports power hint sessions, hidl has no support
+    bool supportsPowerHintSession() override { return mSupportsPowerHint; }
+
+    bool checkPowerHintSessionSupported() {
+        int64_t unused;
+        // Try to get preferred rate to determine if hint sessions are supported
+        // We check for isOk not EX_UNSUPPORTED_OPERATION to lump other errors
+        return mPowerHal->getHintSessionPreferredRate(&unused).isOk();
+    }
+
+    bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; }
+
+    void closePowerHintSession() {
+        if (mPowerHintSession != nullptr) {
+            mPowerHintSession->close();
+            mPowerHintSession = nullptr;
+        }
+    }
+
+    void restartPowerHintSession() {
+        closePowerHintSession();
+        startPowerHintSession();
+    }
+
+    void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override {
+        if (threadIds != mPowerHintThreadIds) {
+            mPowerHintThreadIds = threadIds;
+            if (isPowerHintSessionRunning()) {
+                restartPowerHintSession();
+            }
+        }
+    }
+
+    bool startPowerHintSession() override {
+        if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) {
+            ALOGV("Cannot start power hint session, skipping");
+            return false;
+        }
+        auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
+                                                mPowerHintThreadIds, mTargetDuration,
+                                                &mPowerHintSession);
+        if (!ret.isOk()) {
+            ALOGW("Failed to start power hint session with error: %s",
+                  ret.exceptionToString(ret.exceptionCode()).c_str());
+        } else {
+            mLastTargetDurationSent = mTargetDuration;
+        }
+        return isPowerHintSessionRunning();
+    }
+
+    bool shouldSetTargetDuration(int64_t targetDurationNanos) {
+        // report if the change in target from our last submission to now exceeds the threshold
+        return abs(1.0 -
+                   static_cast<double>(mLastTargetDurationSent) /
+                           static_cast<double>(targetDurationNanos)) >=
+                kAllowedTargetDeviationPercent;
+    }
+
+    void setTargetWorkDuration(int64_t targetDurationNanos) override {
+        ATRACE_CALL();
+        mTargetDuration = targetDurationNanos;
+        if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
+        if (!sNormalizeTarget && shouldSetTargetDuration(targetDurationNanos) &&
+            isPowerHintSessionRunning()) {
+            if (mLastActualDurationSent.has_value()) {
+                // update the error term here since we are actually sending an update to powerhal
+                if (sTraceHintSessionData)
+                    ATRACE_INT64("Target error term",
+                                 targetDurationNanos - *mLastActualDurationSent);
+            }
+            ALOGV("Sending target time: %lld ns", static_cast<long long>(targetDurationNanos));
+            mLastTargetDurationSent = targetDurationNanos;
+            auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
+            if (!ret.isOk()) {
+                ALOGW("Failed to set power hint target work duration with error: %s",
+                      ret.exceptionMessage().c_str());
+                mShouldReconnectHal = true;
+            }
+        }
+    }
+
+    bool shouldReportActualDurationsNow() {
+        // report if we have never reported before or are approaching a stale session
+        if (!mLastActualDurationSent.has_value() ||
+            (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
+            return true;
+        }
+
+        if (!mActualDuration.has_value()) {
+            return false;
+        }
+
+        // duration of most recent timing
+        const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
+        // duration of the last timing actually reported to the powerhal
+        const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
+
+        // report if the change in duration from then to now exceeds the threshold
+        return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
+                kAllowedActualDeviationPercent;
+    }
+
+    void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override {
+        ATRACE_CALL();
+
+        if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+            ALOGV("Failed to send actual work duration, skipping");
+            return;
+        }
+
+        WorkDuration duration;
+        duration.durationNanos = actualDurationNanos;
+        mActualDuration = actualDurationNanos;
+
+        // normalize the sent values to a pre-set target
+        if (sNormalizeTarget) {
+            duration.durationNanos += mLastTargetDurationSent - mTargetDuration;
+        }
+        duration.timeStampNanos = timeStampNanos;
+        mPowerHintQueue.push_back(duration);
+
+        long long targetNsec = mTargetDuration;
+        long long durationNsec = actualDurationNanos;
+
+        if (sTraceHintSessionData) {
+            ATRACE_INT64("Measured duration", durationNsec);
+            ATRACE_INT64("Target error term", targetNsec - durationNsec);
+        }
+
+        ALOGV("Sending actual work duration of: %lld on target: %lld with error: %lld",
+              durationNsec, targetNsec, targetNsec - durationNsec);
+
+        // This rate limiter queues similar duration reports to the powerhal into
+        // batches to avoid excessive binder calls. The criteria to send a given batch
+        // are outlined in shouldReportActualDurationsNow()
+        if (shouldReportActualDurationsNow()) {
+            ALOGV("Sending hint update batch");
+            mLastActualReportTimestamp = systemTime();
+            auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
+            if (!ret.isOk()) {
+                ALOGW("Failed to report actual work durations with error: %s",
+                      ret.exceptionMessage().c_str());
+                mShouldReconnectHal = true;
+            }
+            mPowerHintQueue.clear();
+            // we save the non-normalized value here to detect % changes
+            mLastActualDurationSent = actualDurationNanos;
+        }
+    }
+
+    bool shouldReconnectHAL() override { return mShouldReconnectHal; }
+
+    std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }
+
+    std::optional<int64_t> getTargetWorkDuration() override { return mTargetDuration; }
+
 private:
     const sp<IPower> mPowerHal = nullptr;
     bool mHasExpensiveRendering = false;
     bool mHasDisplayUpdateImminent = false;
+    // Used to indicate an error state and need for reconstruction
+    bool mShouldReconnectHal = false;
+    // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+    sp<IPowerHintSession> mPowerHintSession = nullptr;
+    // Queue of actual durations saved to report
+    std::vector<WorkDuration> mPowerHintQueue;
+    // The latest un-normalized values we have received for target and actual
+    int64_t mTargetDuration = kDefaultTarget.count();
+    std::optional<int64_t> mActualDuration;
+    // The list of thread ids, stored so we can restart the session from this class if needed
+    std::vector<int32_t> mPowerHintThreadIds;
+    bool mSupportsPowerHint;
+    // Keep track of the last messages sent for rate limiter change detection
+    std::optional<int64_t> mLastActualDurationSent;
+    // timestamp of the last report we sent, used to avoid stale sessions
+    int64_t mLastActualReportTimestamp = 0;
+    int64_t mLastTargetDurationSent = kDefaultTarget.count();
+    // Whether to normalize all the actual values as error terms relative to a constant target
+    // This saves a binder call by not setting the target, and should not affect the pid values
+    static const bool sNormalizeTarget;
+    // Whether we should emit ATRACE_INT data for hint sessions
+    static const bool sTraceHintSessionData;
+    // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
+    static constexpr double kAllowedActualDeviationPercent = 0.1;
+    // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
+    static constexpr double kAllowedTargetDeviationPercent = 0.05;
+    // Target used for init and normalization, the actual value does not really matter
+    static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
+    // amount of time after the last message was sent before the session goes stale
+    // actually 100ms but we use 80 here to ideally avoid going stale
+    static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
 };
 
+const bool AidlPowerHalWrapper::sTraceHintSessionData =
+        base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
+
+const bool AidlPowerHalWrapper::sNormalizeTarget =
+        base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), true);
+
 PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
     static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
     static bool sHasHal = true;
@@ -246,6 +544,15 @@
         return nullptr;
     }
 
+    // grab old hint session values before we destroy any existing wrapper
+    std::vector<int32_t> oldPowerHintSessionThreadIds;
+    std::optional<int64_t> oldTargetWorkDuration;
+
+    if (sHalWrapper != nullptr) {
+        oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
+        oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+    }
+
     // If we used to have a HAL, but it stopped responding, attempt to reconnect
     if (mReconnectPowerHal) {
         sHalWrapper = nullptr;
@@ -253,15 +560,34 @@
     }
 
     if (sHalWrapper != nullptr) {
-        return sHalWrapper.get();
+        auto wrapper = sHalWrapper.get();
+        // if the wrapper is fine, return it, but if it indicates a reconnect, remake it
+        if (!wrapper->shouldReconnectHAL()) {
+            return wrapper;
+        }
+        sHalWrapper = nullptr;
     }
 
+    // at this point, we know for sure there is no running session
+    mPowerHintSessionRunning = false;
+
     // First attempt to connect to the AIDL Power HAL
     sHalWrapper = AidlPowerHalWrapper::connect();
 
     // If that didn't succeed, attempt to connect to the HIDL Power HAL
     if (sHalWrapper == nullptr) {
         sHalWrapper = HidlPowerHalWrapper::connect();
+    } else { // if AIDL, pass on any existing hint session values
+        // thread ids always safe to set
+        sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+        // only set duration and start if duration is defined
+        if (oldTargetWorkDuration.has_value()) {
+            sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+            // only start if possible to run and both threadids and duration are defined
+            if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
+                mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+            }
+        }
     }
 
     // If we make it to this point and still don't have a HAL, it's unlikely we
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index f2d0766..28d28f4 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <atomic>
+#include <chrono>
 #include <unordered_set>
 
 #include <utils/Mutex.h>
@@ -24,6 +25,8 @@
 #include "../Scheduler/OneShotTimer.h"
 #include "DisplayIdentification.h"
 
+using namespace std::chrono_literals;
+
 namespace android {
 
 class SurfaceFlinger;
@@ -40,6 +43,13 @@
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
     virtual bool isUsingExpensiveRendering() = 0;
     virtual void notifyDisplayUpdateImminent() = 0;
+    virtual bool usePowerHintSession() = 0;
+    virtual bool supportsPowerHintSession() = 0;
+    virtual bool isPowerHintSessionRunning() = 0;
+    virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
+    virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0;
+    virtual void enablePowerHint(bool enabled) = 0;
+    virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
 };
 
 namespace impl {
@@ -54,6 +64,17 @@
 
         virtual bool setExpensiveRendering(bool enabled) = 0;
         virtual bool notifyDisplayUpdateImminent() = 0;
+        virtual bool supportsPowerHintSession() = 0;
+        virtual bool isPowerHintSessionRunning() = 0;
+        virtual void restartPowerHintSession() = 0;
+        virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
+        virtual bool startPowerHintSession() = 0;
+        virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
+        virtual void sendActualWorkDuration(int64_t actualDurationNanos,
+                                            nsecs_t timeStampNanos) = 0;
+        virtual bool shouldReconnectHAL() = 0;
+        virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
+        virtual std::optional<int64_t> getTargetWorkDuration() = 0;
     };
 
     PowerAdvisor(SurfaceFlinger& flinger);
@@ -62,8 +83,15 @@
     void init() override;
     void onBootFinished() override;
     void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
-    bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; }
+    bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
     void notifyDisplayUpdateImminent() override;
+    bool usePowerHintSession() override;
+    bool supportsPowerHintSession() override;
+    bool isPowerHintSessionRunning() override;
+    void setTargetWorkDuration(int64_t targetDurationNanos) override;
+    void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override;
+    void enablePowerHint(bool enabled) override;
+    bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
 
 private:
     HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
@@ -71,6 +99,15 @@
     std::mutex mPowerHalMutex;
 
     std::atomic_bool mBootFinished = false;
+    std::optional<bool> mPowerHintEnabled;
+    std::optional<bool> mSupportsPowerHint;
+    bool mPowerHintSessionRunning = false;
+
+    // An adjustable safety margin which moves the "target" earlier to allow flinger to
+    // go a bit over without dropping a frame, especially since we can't measure
+    // the exact time HWC finishes composition so "actual" durations are measured
+    // from the end of present() instead, which is a bit later.
+    static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms;
 
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 82a9ae2..b4fb51f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -21,20 +21,18 @@
 // #define LOG_NDEBUG 0
 #include "VirtualDisplaySurface.h"
 
-#include <inttypes.h>
+#include <cinttypes>
 
 #include "HWComposer.h"
 #include "SurfaceFlinger.h"
 
+#include <ftl/Flags.h>
+#include <ftl/enum.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
 #include <gui/IProducerListener.h>
 #include <system/window.h>
 
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
 #define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
         mDisplayName.c_str(), ##__VA_ARGS__)
 #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
@@ -42,20 +40,11 @@
 #define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
         mDisplayName.c_str(), ##__VA_ARGS__)
 
-static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) {
-    switch (type) {
-        case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
-            return "UNKNOWN";
-        case compositionengine::DisplaySurface::COMPOSITION_GPU:
-            return "GPU";
-        case compositionengine::DisplaySurface::COMPOSITION_HWC:
-            return "HWC";
-        case compositionengine::DisplaySurface::COMPOSITION_MIXED:
-            return "MIXED";
-        default:
-            return "<INVALID>";
-    }
-}
+#define UNSUPPORTED()                                               \
+    VDS_LOGE("%s: Invalid operation on virtual display", __func__); \
+    return INVALID_OPERATION
+
+namespace android {
 
 VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
                                              const sp<IGraphicBufferProducer>& sink,
@@ -76,14 +65,10 @@
         mQueueBufferOutput(),
         mSinkBufferWidth(0),
         mSinkBufferHeight(0),
-        mCompositionType(COMPOSITION_UNKNOWN),
         mFbFence(Fence::NO_FENCE),
         mOutputFence(Fence::NO_FENCE),
         mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
         mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
-        mDbgState(DBG_STATE_IDLE),
-        mDbgLastCompositionType(COMPOSITION_UNKNOWN),
-        mMustRecompose(false),
         mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) {
     mSource[SOURCE_SINK] = sink;
     mSource[SOURCE_SCRATCH] = bqProducer;
@@ -131,9 +116,9 @@
 
     mMustRecompose = mustRecompose;
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
-            "Unexpected beginFrame() in %s state", dbgStateStr());
-    mDbgState = DBG_STATE_BEGUN;
+    VDS_LOGW_IF(mDebugState != DebugState::Idle, "Unexpected %s in %s state", __func__,
+                ftl::enum_string(mDebugState).c_str());
+    mDebugState = DebugState::Begun;
 
     return refreshOutputBuffer();
 }
@@ -143,12 +128,12 @@
         return NO_ERROR;
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
-            "Unexpected prepareFrame() in %s state", dbgStateStr());
-    mDbgState = DBG_STATE_PREPARED;
+    VDS_LOGW_IF(mDebugState != DebugState::Begun, "Unexpected %s in %s state", __func__,
+                ftl::enum_string(mDebugState).c_str());
+    mDebugState = DebugState::Prepared;
 
     mCompositionType = compositionType;
-    if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) {
+    if (mForceHwcCopy && mCompositionType == CompositionType::Gpu) {
         // Some hardware can do RGB->YUV conversion more efficiently in hardware
         // controlled by HWC than in hardware controlled by the video encoder.
         // Forcing GPU-composed frames to go through an extra copy by the HWC
@@ -157,16 +142,16 @@
         //
         // On the other hand, when the consumer prefers RGB or can consume RGB
         // inexpensively, this forces an unnecessary copy.
-        mCompositionType = COMPOSITION_MIXED;
+        mCompositionType = CompositionType::Mixed;
     }
 
-    if (mCompositionType != mDbgLastCompositionType) {
-        VDS_LOGV("prepareFrame: composition type changed to %s",
-                dbgCompositionTypeStr(mCompositionType));
-        mDbgLastCompositionType = mCompositionType;
+    if (mCompositionType != mDebugLastCompositionType) {
+        VDS_LOGV("%s: composition type changed to %s", __func__,
+                 toString(mCompositionType).c_str());
+        mDebugLastCompositionType = mCompositionType;
     }
 
-    if (mCompositionType != COMPOSITION_GPU &&
+    if (mCompositionType != CompositionType::Gpu &&
         (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
         // We must have just switched from GPU-only to MIXED or HWC
         // composition. Stop using the format and usage requested by the GPU
@@ -191,33 +176,32 @@
         return NO_ERROR;
     }
 
-    if (mCompositionType == COMPOSITION_HWC) {
-        VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
-                "Unexpected advanceFrame() in %s state on HWC frame",
-                dbgStateStr());
+    if (mCompositionType == CompositionType::Hwc) {
+        VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state on HWC frame",
+                    __func__, ftl::enum_string(mDebugState).c_str());
     } else {
-        VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE,
-                    "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr());
+        VDS_LOGW_IF(mDebugState != DebugState::GpuDone,
+                    "Unexpected %s in %s state on GPU/MIXED frame", __func__,
+                    ftl::enum_string(mDebugState).c_str());
     }
-    mDbgState = DBG_STATE_HWC;
+    mDebugState = DebugState::Hwc;
 
     if (mOutputProducerSlot < 0 ||
-            (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
+        (mCompositionType != CompositionType::Hwc && mFbProducerSlot < 0)) {
         // Last chance bailout if something bad happened earlier. For example,
         // in a graphics API configuration, if the sink disappears then dequeueBuffer
         // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
         // will soldier on. So we end up here without a buffer. There should
         // be lots of scary messages in the log just before this.
-        VDS_LOGE("advanceFrame: no buffer, bailing out");
+        VDS_LOGE("%s: no buffer, bailing out", __func__);
         return NO_MEMORY;
     }
 
     sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
             mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
     sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
-    VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
-            mFbProducerSlot, fbBuffer.get(),
-            mOutputProducerSlot, outBuffer.get());
+    VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
+             mOutputProducerSlot, outBuffer.get());
 
     const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
     LOG_FATAL_IF(!halDisplayId);
@@ -245,16 +229,16 @@
         return;
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
-            "Unexpected onFrameCommitted() in %s state", dbgStateStr());
-    mDbgState = DBG_STATE_IDLE;
+    VDS_LOGW_IF(mDebugState != DebugState::Hwc, "Unexpected %s in %s state", __func__,
+                ftl::enum_string(mDebugState).c_str());
+    mDebugState = DebugState::Idle;
 
     sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId);
-    if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
+    if (mCompositionType == CompositionType::Mixed && mFbProducerSlot >= 0) {
         // release the scratch buffer back to the pool
         Mutex::Autolock lock(mMutex);
         int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
-        VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
+        VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot);
         addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot],
                 retireFence);
         releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]);
@@ -263,7 +247,7 @@
     if (mOutputProducerSlot >= 0) {
         int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
         QueueBufferOutput qbo;
-        VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
+        VDS_LOGV("%s: queue sink sslot=%d", __func__, sslot);
         if (mMustRecompose) {
             status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
                     QueueBufferInput(
@@ -308,8 +292,8 @@
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot,
-                dbgStateStr());
+    VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s pslot=%d in %s state", __func__,
+                pslot, ftl::enum_string(mDebugState).c_str());
 
     *outBuf = mProducerBuffers[pslot];
     return NO_ERROR;
@@ -334,8 +318,8 @@
     if (result < 0)
         return result;
     int pslot = mapSource2ProducerSlot(source, *sslot);
-    VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d",
-            dbgSourceStr(source), *sslot, pslot, result);
+    VDS_LOGV("%s(%s): sslot=%d pslot=%d result=%d", __func__, ftl::enum_string(source).c_str(),
+             *sslot, pslot, result);
     uint64_t sourceBit = static_cast<uint64_t>(source) << pslot;
 
     // reset producer slot reallocation flag
@@ -363,10 +347,9 @@
             mSource[source]->cancelBuffer(*sslot, *fence);
             return result;
         }
-        VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64,
-                dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
-                mProducerBuffers[pslot]->getPixelFormat(),
-                mProducerBuffers[pslot]->getUsage());
+        VDS_LOGV("%s(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64, __func__,
+                 ftl::enum_string(source).c_str(), pslot, mProducerBuffers[pslot].get(),
+                 mProducerBuffers[pslot]->getPixelFormat(), mProducerBuffers[pslot]->getUsage());
 
         // propagate reallocation to VDS consumer
         mProducerSlotNeedReallocation |= 1ULL << pslot;
@@ -384,11 +367,11 @@
                                                    outTimestamps);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
-            "Unexpected dequeueBuffer() in %s state", dbgStateStr());
-    mDbgState = DBG_STATE_GPU;
+    VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state", __func__,
+                ftl::enum_string(mDebugState).c_str());
+    mDebugState = DebugState::Gpu;
 
-    VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage);
+    VDS_LOGV("%s %dx%d fmt=%d usage=%#" PRIx64, __func__, w, h, format, usage);
 
     status_t result = NO_ERROR;
     Source source = fbSourceForCompositionType(mCompositionType);
@@ -401,7 +384,7 @@
             // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
             // will soldier on. So we end up here without a buffer. There should
             // be lots of scary messages in the log just before this.
-            VDS_LOGE("dequeueBuffer: no buffer, bailing out");
+            VDS_LOGE("%s: no buffer, bailing out", __func__);
             return NO_MEMORY;
         }
 
@@ -417,12 +400,11 @@
                 (format != 0 && format != buf->getPixelFormat()) ||
                 (w != 0 && w != mSinkBufferWidth) ||
                 (h != 0 && h != mSinkBufferHeight)) {
-            VDS_LOGV("dequeueBuffer: dequeueing new output buffer: "
-                    "want %dx%d fmt=%d use=%#" PRIx64 ", "
-                    "have %dx%d fmt=%d use=%#" PRIx64,
-                    w, h, format, usage,
-                    mSinkBufferWidth, mSinkBufferHeight,
-                    buf->getPixelFormat(), buf->getUsage());
+            VDS_LOGV("%s: dequeueing new output buffer: "
+                     "want %dx%d fmt=%d use=%#" PRIx64 ", "
+                     "have %dx%d fmt=%d use=%#" PRIx64,
+                     __func__, w, h, format, usage, mSinkBufferWidth, mSinkBufferHeight,
+                     buf->getPixelFormat(), buf->getUsage());
             mOutputFormat = format;
             mOutputUsage = usage;
             result = refreshOutputBuffer();
@@ -452,21 +434,16 @@
     return result;
 }
 
-status_t VirtualDisplaySurface::detachBuffer(int /* slot */) {
-    VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::detachBuffer(int) {
+    UNSUPPORTED();
 }
 
-status_t VirtualDisplaySurface::detachNextBuffer(
-        sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) {
-    VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::detachNextBuffer(sp<GraphicBuffer>*, sp<Fence>*) {
+    UNSUPPORTED();
 }
 
-status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
-        const sp<GraphicBuffer>& /* buffer */) {
-    VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::attachBuffer(int*, const sp<GraphicBuffer>&) {
+    UNSUPPORTED();
 }
 
 status_t VirtualDisplaySurface::queueBuffer(int pslot,
@@ -475,14 +452,14 @@
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
-                dbgStateStr());
-    mDbgState = DBG_STATE_GPU_DONE;
+    VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__,
+                pslot, ftl::enum_string(mDebugState).c_str());
+    mDebugState = DebugState::GpuDone;
 
-    VDS_LOGV("queueBuffer pslot=%d", pslot);
+    VDS_LOGV("%s pslot=%d", __func__, pslot);
 
     status_t result;
-    if (mCompositionType == COMPOSITION_MIXED) {
+    if (mCompositionType == CompositionType::Mixed) {
         // Queue the buffer back into the scratch pool
         QueueBufferOutput scratchQBO;
         int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot);
@@ -498,15 +475,15 @@
         if (result != NO_ERROR)
             return result;
         VDS_LOGW_IF(item.mSlot != sslot,
-                "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d",
-                item.mSlot, sslot);
+                    "%s: acquired sslot %d from SCRATCH after queueing sslot %d", __func__,
+                    item.mSlot, sslot);
         mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mSlot);
         mFbFence = mSlots[item.mSlot].mFence;
 
     } else {
-        LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU,
-                     "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(),
-                     dbgCompositionTypeStr(mCompositionType));
+        LOG_FATAL_IF(mCompositionType != CompositionType::Gpu,
+                     "Unexpected %s in state %s for composition type %s", __func__,
+                     ftl::enum_string(mDebugState).c_str(), toString(mCompositionType).c_str());
 
         // Extract the GPU release fence for HWC to acquire
         int64_t timestamp;
@@ -533,9 +510,9 @@
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
-                dbgStateStr());
-    VDS_LOGV("cancelBuffer pslot=%d", pslot);
+    VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__,
+                pslot, ftl::enum_string(mDebugState).c_str());
+    VDS_LOGV("%s pslot=%d", __func__, pslot);
     Source source = fbSourceForCompositionType(mCompositionType);
     return mSource[source]->cancelBuffer(
             mapProducer2SourceSlot(source, pslot), fence);
@@ -573,8 +550,8 @@
     return mSource[SOURCE_SINK]->disconnect(api, mode);
 }
 
-status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) {
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>&) {
+    UNSUPPORTED();
 }
 
 void VirtualDisplaySurface::allocateBuffers(uint32_t /* width */,
@@ -586,40 +563,32 @@
     return INVALID_OPERATION;
 }
 
-status_t VirtualDisplaySurface::setGenerationNumber(uint32_t /* generation */) {
-    ALOGE("setGenerationNumber not supported on VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setGenerationNumber(uint32_t) {
+    UNSUPPORTED();
 }
 
 String8 VirtualDisplaySurface::getConsumerName() const {
     return String8("VirtualDisplaySurface");
 }
 
-status_t VirtualDisplaySurface::setSharedBufferMode(bool /*sharedBufferMode*/) {
-    ALOGE("setSharedBufferMode not supported on VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setSharedBufferMode(bool) {
+    UNSUPPORTED();
 }
 
-status_t VirtualDisplaySurface::setAutoRefresh(bool /*autoRefresh*/) {
-    ALOGE("setAutoRefresh not supported on VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setAutoRefresh(bool) {
+    UNSUPPORTED();
 }
 
-status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) {
-    ALOGE("setDequeueTimeout not supported on VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t) {
+    UNSUPPORTED();
 }
 
-status_t VirtualDisplaySurface::getLastQueuedBuffer(
-        sp<GraphicBuffer>* /*outBuffer*/, sp<Fence>* /*outFence*/,
-        float[16] /* outTransformMatrix*/) {
-    ALOGE("getLastQueuedBuffer not supported on VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::getLastQueuedBuffer(sp<GraphicBuffer>*, sp<Fence>*, float[16]) {
+    UNSUPPORTED();
 }
 
-status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const {
-    ALOGE("getUniqueId not supported on VirtualDisplaySurface");
-    return INVALID_OPERATION;
+status_t VirtualDisplaySurface::getUniqueId(uint64_t*) const {
+    UNSUPPORTED();
 }
 
 status_t VirtualDisplaySurface::getConsumerUsage(uint64_t* outUsage) const {
@@ -633,7 +602,7 @@
 }
 
 void VirtualDisplaySurface::resetPerFrameState() {
-    mCompositionType = COMPOSITION_UNKNOWN;
+    mCompositionType = CompositionType::Unknown;
     mFbFence = Fence::NO_FENCE;
     mOutputFence = Fence::NO_FENCE;
     mOutputProducerSlot = -1;
@@ -682,39 +651,16 @@
     return mapSource2ProducerSlot(source, pslot);
 }
 
-VirtualDisplaySurface::Source
-VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
-    return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
+auto VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) -> Source {
+    return type == CompositionType::Mixed ? SOURCE_SCRATCH : SOURCE_SINK;
 }
 
-const char* VirtualDisplaySurface::dbgStateStr() const {
-    switch (mDbgState) {
-        case DBG_STATE_IDLE:
-            return "IDLE";
-        case DBG_STATE_PREPARED:
-            return "PREPARED";
-        case DBG_STATE_GPU:
-            return "GPU";
-        case DBG_STATE_GPU_DONE:
-            return "GPU_DONE";
-        case DBG_STATE_HWC:
-            return "HWC";
-        default:
-            return "INVALID";
-    }
+std::string VirtualDisplaySurface::toString(CompositionType type) {
+    using namespace std::literals;
+    return type == CompositionType::Unknown ? "Unknown"s : Flags(type).string();
 }
 
-const char* VirtualDisplaySurface::dbgSourceStr(Source s) {
-    switch (s) {
-        case SOURCE_SINK:    return "SINK";
-        case SOURCE_SCRATCH: return "SCRATCH";
-        default:             return "INVALID";
-    }
-}
-
-// ---------------------------------------------------------------------------
 } // namespace android
-// ---------------------------------------------------------------------------
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index bbb6306..7720713 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
-#define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
+#pragma once
 
 #include <optional>
 #include <string>
@@ -28,9 +27,7 @@
 
 #include "DisplayIdentification.h"
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
 
 class HWComposer;
 class IProducerListener;
@@ -94,7 +91,13 @@
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
 
 private:
-    enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1};
+    enum Source : size_t {
+        SOURCE_SINK = 0,
+        SOURCE_SCRATCH = 1,
+
+        ftl_first = SOURCE_SINK,
+        ftl_last = SOURCE_SCRATCH,
+    };
 
     virtual ~VirtualDisplaySurface();
 
@@ -133,6 +136,8 @@
     // Utility methods
     //
     static Source fbSourceForCompositionType(CompositionType);
+    static std::string toString(CompositionType);
+
     status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*);
     void updateQueueBufferOutput(QueueBufferOutput&&);
     void resetPerFrameState();
@@ -197,7 +202,7 @@
 
     // Composition type and graphics buffer source for the current frame.
     // Valid after prepareFrame(), cleared in onFrameCommitted.
-    CompositionType mCompositionType;
+    CompositionType mCompositionType = CompositionType::Unknown;
 
     // mFbFence is the fence HWC should wait for before reading the framebuffer
     // target buffer.
@@ -219,47 +224,42 @@
     // +-----------+-------------------+-------------+
     // | State     | Event             || Next State |
     // +-----------+-------------------+-------------+
-    // | IDLE      | beginFrame        || BEGUN      |
-    // | BEGUN     | prepareFrame      || PREPARED   |
-    // | PREPARED  | dequeueBuffer [1] || GPU        |
-    // | PREPARED  | advanceFrame [2]  || HWC        |
-    // | GPU       | queueBuffer       || GPU_DONE   |
-    // | GPU_DONE  | advanceFrame      || HWC        |
-    // | HWC       | onFrameCommitted  || IDLE       |
+    // | Idle      | beginFrame        || Begun      |
+    // | Begun     | prepareFrame      || Prepared   |
+    // | Prepared  | dequeueBuffer [1] || Gpu        |
+    // | Prepared  | advanceFrame [2]  || Hwc        |
+    // | Gpu       | queueBuffer       || GpuDone    |
+    // | GpuDone   | advanceFrame      || Hwc        |
+    // | Hwc       | onFrameCommitted  || Idle       |
     // +-----------+-------------------++------------+
-    // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames.
-    // [2] COMPOSITION_HWC frames.
+    // [1] CompositionType::Gpu and CompositionType::Mixed frames.
+    // [2] CompositionType::Hwc frames.
     //
-    enum DbgState {
+    enum class DebugState {
         // no buffer dequeued, don't know anything about the next frame
-        DBG_STATE_IDLE,
+        Idle,
         // output buffer dequeued, framebuffer source not yet known
-        DBG_STATE_BEGUN,
+        Begun,
         // output buffer dequeued, framebuffer source known but not provided
         // to GPU yet.
-        DBG_STATE_PREPARED,
+        Prepared,
         // GPU driver has a buffer dequeued
-        DBG_STATE_GPU,
+        Gpu,
         // GPU driver has queued the buffer, we haven't sent it to HWC yet
-        DBG_STATE_GPU_DONE,
+        GpuDone,
         // HWC has the buffer for this frame
-        DBG_STATE_HWC,
+        Hwc,
+
+        ftl_last = Hwc
     };
-    DbgState mDbgState;
-    CompositionType mDbgLastCompositionType;
+    DebugState mDebugState = DebugState::Idle;
+    CompositionType mDebugLastCompositionType = CompositionType::Unknown;
 
-    const char* dbgStateStr() const;
-    static const char* dbgSourceStr(Source s);
-
-    bool mMustRecompose;
+    bool mMustRecompose = false;
 
     compositionengine::impl::HwcBufferCache mHwcBufferCache;
 
     bool mForceHwcCopy;
 };
 
-// ---------------------------------------------------------------------------
 } // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
index 86c6b21..cc85352 100644
--- a/services/surfaceflinger/EffectLayer.cpp
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -109,7 +109,8 @@
 
     auto* compositionState = editCompositionState();
     compositionState->color = getColor();
-    compositionState->compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    compositionState->compositionType =
+            aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
 }
 
 sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const {
@@ -136,8 +137,7 @@
 
 sp<Layer> EffectLayer::createClone() {
     sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer(
-            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
-                              LayerMetadata()));
+            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
     layer->setInitialValuesForClone(this);
     return layer;
 }
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
new file mode 100644
index 0000000..7602e6d
--- /dev/null
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FlagManager.h"
+
+#include <SurfaceFlingerProperties.sysprop.h>
+#include <android-base/parsebool.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+#include <cinttypes>
+
+namespace android {
+static constexpr const char* kExperimentNamespace = "surface_flinger_native_boot";
+static constexpr const int64_t kDemoFlag = -1;
+
+FlagManager::~FlagManager() = default;
+
+void FlagManager::dump(std::string& result) const {
+    base::StringAppendF(&result, "FlagManager values: \n");
+    base::StringAppendF(&result, "demo_flag: %" PRId64 "\n", demo_flag());
+    base::StringAppendF(&result, "use_adpf_cpu_hint: %s\n", use_adpf_cpu_hint() ? "true" : "false");
+}
+
+namespace {
+template <typename T>
+std::optional<T> doParse(const char* str);
+
+template <>
+[[maybe_unused]] std::optional<int32_t> doParse(const char* str) {
+    int32_t ret;
+    return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
+}
+
+template <>
+[[maybe_unused]] std::optional<int64_t> doParse(const char* str) {
+    int64_t ret;
+    return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
+}
+
+template <>
+[[maybe_unused]] std::optional<bool> doParse(const char* str) {
+    base::ParseBoolResult parseResult = base::ParseBool(str);
+    switch (parseResult) {
+        case base::ParseBoolResult::kTrue:
+            return std::make_optional(true);
+        case base::ParseBoolResult::kFalse:
+            return std::make_optional(false);
+        case base::ParseBoolResult::kError:
+            return std::nullopt;
+    }
+}
+} // namespace
+
+std::string FlagManager::getServerConfigurableFlag(const std::string& experimentFlagName) const {
+    return server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace,
+                                                                experimentFlagName, "");
+}
+
+template int32_t FlagManager::getValue<int32_t>(const std::string&, std::optional<int32_t>,
+                                                int32_t) const;
+template int64_t FlagManager::getValue<int64_t>(const std::string&, std::optional<int64_t>,
+                                                int64_t) const;
+template bool FlagManager::getValue<bool>(const std::string&, std::optional<bool>, bool) const;
+template <typename T>
+T FlagManager::getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
+                        T defaultValue) const {
+    // System property takes precedence over the experiment config server value.
+    if (systemPropertyOpt.has_value()) {
+        return *systemPropertyOpt;
+    }
+    std::string str = getServerConfigurableFlag(experimentFlagName);
+    return str.empty() ? defaultValue : doParse<T>(str.c_str()).value_or(defaultValue);
+}
+
+int64_t FlagManager::demo_flag() const {
+    std::optional<int64_t> sysPropVal = std::nullopt;
+    return getValue("DemoFeature__demo_flag", sysPropVal, kDemoFlag);
+}
+
+bool FlagManager::use_adpf_cpu_hint() const {
+    std::optional<bool> sysPropVal = std::nullopt;
+    return getValue("AdpfFeature__adpf_cpu_hint", sysPropVal, false);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
new file mode 100644
index 0000000..24d83a2
--- /dev/null
+++ b/services/surfaceflinger/FlagManager.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace android {
+// Manages flags for SurfaceFlinger, including default values, system properties, and Mendel
+// experiment configuration values.
+class FlagManager {
+public:
+    FlagManager() = default;
+    virtual ~FlagManager();
+    void dump(std::string& result) const;
+
+    int64_t demo_flag() const;
+
+    bool use_adpf_cpu_hint() const;
+
+private:
+    friend class FlagManagerTest;
+
+    // Wrapper for mocking in test.
+    virtual std::string getServerConfigurableFlag(const std::string& experimentFlagName) const;
+
+    template <typename T>
+    T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
+               T defaultValue) const;
+};
+} // namespace android
diff --git a/services/surfaceflinger/Fps.h b/services/surfaceflinger/Fps.h
deleted file mode 100644
index e9f06e5..0000000
--- a/services/surfaceflinger/Fps.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cmath>
-#include <ostream>
-#include <string>
-
-#include <android-base/stringprintf.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-// Value which represents "frames per second". This class is a wrapper around
-// float, providing some useful utilities, such as comparisons with tolerance
-// and converting between period duration and frequency.
-class Fps {
-public:
-    static constexpr Fps fromPeriodNsecs(nsecs_t period) { return Fps(1e9f / period, period); }
-
-    Fps() = default;
-    explicit constexpr Fps(float fps)
-          : fps(fps), period(fps == 0.0f ? 0 : static_cast<nsecs_t>(1e9f / fps)) {}
-
-    constexpr float getValue() const { return fps; }
-
-    constexpr nsecs_t getPeriodNsecs() const { return period; }
-
-    bool equalsWithMargin(const Fps& other) const { return std::abs(fps - other.fps) < kMargin; }
-
-    // DO NOT use for std::sort. Instead use comparesLess().
-    bool lessThanWithMargin(const Fps& other) const { return fps + kMargin < other.fps; }
-
-    bool greaterThanWithMargin(const Fps& other) const { return fps > other.fps + kMargin; }
-
-    bool lessThanOrEqualWithMargin(const Fps& other) const { return !greaterThanWithMargin(other); }
-
-    bool greaterThanOrEqualWithMargin(const Fps& other) const { return !lessThanWithMargin(other); }
-
-    bool isValid() const { return fps > 0.0f; }
-
-    int getIntValue() const { return static_cast<int>(std::round(fps)); }
-
-    // Use this comparator for sorting. Using a comparator with margins can
-    // cause std::sort to crash.
-    inline static bool comparesLess(const Fps& left, const Fps& right) {
-        return left.fps < right.fps;
-    }
-
-    // Compares two FPS with margin.
-    // Transitivity is not guaranteed, i.e. a==b and b==c doesn't imply a==c.
-    // DO NOT use with hash maps. Instead use EqualsInBuckets.
-    struct EqualsWithMargin {
-        bool operator()(const Fps& left, const Fps& right) const {
-            return left.equalsWithMargin(right);
-        }
-    };
-
-    // Equals comparator which can be used with hash maps.
-    // It's guaranteed that if two elements are equal, then their hashes are equal.
-    struct EqualsInBuckets {
-        bool operator()(const Fps& left, const Fps& right) const {
-            return left.getBucket() == right.getBucket();
-        }
-    };
-
-    inline friend std::string to_string(const Fps& fps) {
-        return base::StringPrintf("%.2ffps", fps.fps);
-    }
-
-    inline friend std::ostream& operator<<(std::ostream& os, const Fps& fps) {
-        return os << to_string(fps);
-    }
-
-private:
-    friend std::hash<android::Fps>;
-
-    constexpr Fps(float fps, nsecs_t period) : fps(fps), period(period) {}
-
-    float getBucket() const { return std::round(fps / kMargin); }
-
-    static constexpr float kMargin = 0.001f;
-    float fps = 0;
-    nsecs_t period = 0;
-};
-
-static_assert(std::is_trivially_copyable_v<Fps>);
-
-} // namespace android
-
-namespace std {
-template <>
-struct hash<android::Fps> {
-    std::size_t operator()(const android::Fps& fps) const {
-        return std::hash<float>()(fps.getBucket());
-    }
-};
-} // namespace std
\ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index bd7b9a5..438b1aa 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -24,6 +24,7 @@
 
 #include "Clock.h"
 #include "FrameTimeline/FrameTimeline.h"
+#include "WpHash.h"
 
 namespace android {
 
@@ -50,11 +51,6 @@
 
 private:
     mutable std::mutex mMutex;
-    struct WpHash {
-        size_t operator()(const wp<IBinder>& p) const {
-            return std::hash<IBinder*>()(p.unsafe_get());
-        }
-    };
 
     struct TrackedListener {
         sp<gui::IFpsListener> listener;
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 10a5833..2d4ec04 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -13,6 +13,9 @@
     srcs: [
         "FrameTimeline.cpp",
     ],
+    header_libs: [
+        "libscheduler_headers",
+    ],
     shared_libs: [
         "android.hardware.graphics.composer@2.4",
         "libbase",
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index c294ff2..86e96d7 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -304,7 +304,7 @@
                            frametimeline::TimelineItem&& predictions,
                            std::shared_ptr<TimeStats> timeStats,
                            JankClassificationThresholds thresholds,
-                           TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode)
+                           TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode gameMode)
       : mToken(frameTimelineInfo.vsyncId),
         mInputEventId(frameTimelineInfo.inputEventId),
         mOwnerPid(ownerPid),
@@ -667,7 +667,8 @@
             packet->set_timestamp(
                     static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta));
         } else {
-            packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
+            packet->set_timestamp(static_cast<uint64_t>(
+                    mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime));
         }
 
         auto* event = packet->set_frame_timeline_event();
@@ -778,7 +779,7 @@
 
 std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
         const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
-        std::string layerName, std::string debugName, bool isBuffer, int32_t gameMode) {
+        std::string layerName, std::string debugName, bool isBuffer, GameMode gameMode) {
     ATRACE_CALL();
     if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
         return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 139f91f..36d6290 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -16,10 +16,17 @@
 
 #pragma once
 
-#include <../Fps.h>
-#include <../TimeStats/TimeStats.h>
+#include <atomic>
+#include <chrono>
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+
 #include <gui/ISurfaceComposer.h>
 #include <gui/JankInfo.h>
+#include <gui/LayerMetadata.h>
 #include <perfetto/trace/android/frame_timeline_event.pbzero.h>
 #include <perfetto/tracing.h>
 #include <ui/FenceTime.h>
@@ -28,8 +35,9 @@
 #include <utils/Timers.h>
 #include <utils/Vector.h>
 
-#include <deque>
-#include <mutex>
+#include <scheduler/Fps.h>
+
+#include "../TimeStats/TimeStats.h"
 
 namespace android::frametimeline {
 
@@ -154,7 +162,7 @@
                  int32_t layerId, std::string layerName, std::string debugName,
                  PredictionState predictionState, TimelineItem&& predictions,
                  std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
-                 TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode);
+                 TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode);
     ~SurfaceFrame() = default;
 
     // Returns std::nullopt if the frame hasn't been classified yet.
@@ -260,7 +268,7 @@
     // buffer(animations)
     bool mIsBuffer;
     // GameMode from the layer. Used in metrics.
-    int32_t mGameMode = 0;
+    GameMode mGameMode = GameMode::Unsupported;
 };
 
 /*
@@ -281,7 +289,7 @@
     virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
             int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
-            int32_t gameMode) = 0;
+            GameMode) = 0;
 
     // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
     // composited into one display frame.
@@ -441,7 +449,7 @@
     std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
             int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
-            int32_t gameMode) override;
+            GameMode) override;
     void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
     void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 671395f..4ada2b6 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -22,6 +22,8 @@
 
 #include <unordered_map>
 
+#include "WpHash.h"
+
 namespace android {
 
 class HdrLayerInfoReporter final : public IBinder::DeathRecipient {
@@ -63,11 +65,6 @@
 
 private:
     mutable std::mutex mMutex;
-    struct WpHash {
-        size_t operator()(const wp<IBinder>& p) const {
-            return std::hash<IBinder*>()(p.unsafe_get());
-        }
-    };
 
     struct TrackedListener {
         sp<gui::IHdrLayerInfoListener> listener;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index da7ff71..5948a78 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -36,6 +36,7 @@
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
+#include <ftl/enum.h>
 #include <gui/BufferItem.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
@@ -45,6 +46,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/types.h>
+#include <ui/DataspaceUtils.h>
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
@@ -87,11 +89,12 @@
 std::atomic<int32_t> Layer::sSequence{1};
 
 Layer::Layer(const LayerCreationArgs& args)
-      : mFlinger(args.flinger),
-        mName(args.name),
+      : sequence(args.sequence.value_or(sSequence++)),
+        mFlinger(args.flinger),
+        mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
         mClientRef(args.client),
-        mWindowType(
-                static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
+        mWindowType(static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))),
+        mLayerCreationFlags(args.flags) {
     uint32_t layerFlags = 0;
     if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
     if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -99,15 +102,13 @@
     if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
         layerFlags |= layer_state_t::eLayerSkipScreenshot;
 
-    mDrawingState.active_legacy.w = args.w;
-    mDrawingState.active_legacy.h = args.h;
     mDrawingState.flags = layerFlags;
     mDrawingState.active_legacy.transform.set(0, 0);
     mDrawingState.crop.makeInvalid();
     mDrawingState.requestedCrop = mDrawingState.crop;
     mDrawingState.z = 0;
     mDrawingState.color.a = 1.0f;
-    mDrawingState.layerStack = 0;
+    mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK;
     mDrawingState.sequence = 0;
     mDrawingState.requested_legacy = mDrawingState.active_legacy;
     mDrawingState.width = UINT32_MAX;
@@ -185,12 +186,10 @@
 }
 
 LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
-                                     uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
+                                     uint32_t flags, LayerMetadata metadata)
       : flinger(flinger),
         client(std::move(client)),
         name(std::move(name)),
-        w(w),
-        h(h),
         flags(flags),
         metadata(std::move(metadata)) {
     IPCThreadState* ipc = IPCThreadState::self();
@@ -207,7 +206,8 @@
  * Layer.  So, the implementation is done in BufferLayer.  When called on a
  * EffectLayer object, it's essentially a NOP.
  */
-void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
+void Layer::onLayerDisplayed(
+        std::shared_future<renderengine::RenderEngineResult> /*futureRenderEngineResult*/) {}
 
 void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
     if (mDrawingState.zOrderRelativeOf == nullptr) {
@@ -393,7 +393,6 @@
 
 void Layer::prepareBasicGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-    const uint32_t layerStack = getLayerStack();
     const auto alpha = static_cast<float>(getAlpha());
     const bool opaque = isOpaque(drawingState);
     const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
@@ -405,9 +404,7 @@
     }
 
     auto* compositionState = editCompositionState();
-    compositionState->layerStackId =
-            (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt;
-    compositionState->internalOnly = getPrimaryDisplayOnly();
+    compositionState->outputFilter = getOutputFilter();
     compositionState->isVisible = isVisible();
     compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
     compositionState->shadowRadius = mEffectiveShadowRadius;
@@ -422,25 +419,13 @@
 
     compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
     compositionState->alpha = alpha;
+    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    compositionState->blurRegions = drawingState.blurRegions;
     compositionState->stretchEffect = getStretchEffect();
 }
 
 void Layer::prepareGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-
-    int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
-    int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0);
-    sp<Layer> parent = mDrawingParent.promote();
-    if (parent.get()) {
-        auto& parentState = parent->getDrawingState();
-        const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
-        const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0);
-        if (parentType > 0 && parentAppId > 0) {
-            type = parentType;
-            appId = parentAppId;
-        }
-    }
-
     auto* compositionState = editCompositionState();
 
     compositionState->geomBufferSize = getBufferSize(drawingState);
@@ -500,6 +485,11 @@
     // If there are no visible region changes, we still need to update blur parameters.
     compositionState->blurRegions = drawingState.blurRegions;
     compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+
+    // Layer framerate is used in caching decisions.
+    // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
+    // LayerFECompositionState where it would be visible to Flattener.
+    compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
 }
 
 void Layer::prepareCursorCompositionState() {
@@ -596,6 +586,8 @@
 
     layerSettings.alpha = alpha;
     layerSettings.sourceDataspace = getDataSpace();
+
+    layerSettings.whitePointNits = targetSettings.whitePointNits;
     switch (targetSettings.blurSetting) {
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
             layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
@@ -658,15 +650,16 @@
     return {*layerSettings};
 }
 
-Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
+aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
+        const DisplayDevice& display) const {
     const auto outputLayer = findOutputLayerForDisplay(&display);
     if (outputLayer == nullptr) {
-        return Hwc2::IComposerClient::Composition::INVALID;
+        return aidl::android::hardware::graphics::composer3::Composition::INVALID;
     }
     if (outputLayer->getState().hwc) {
         return (*outputLayer->getState().hwc).hwcCompositionType;
     } else {
-        return Hwc2::IComposerClient::Composition::CLIENT;
+        return aidl::android::hardware::graphics::composer3::Composition::CLIENT;
     }
 }
 
@@ -731,14 +724,14 @@
     mDrawingState.bufferlessSurfaceFramesTX.clear();
 }
 
-uint32_t Layer::getTransactionFlags(uint32_t flags) {
-    auto ret = mTransactionFlags & flags;
-    mTransactionFlags &= ~flags;
-    return ret;
+uint32_t Layer::clearTransactionFlags(uint32_t mask) {
+    const auto flags = mTransactionFlags & mask;
+    mTransactionFlags &= ~mask;
+    return flags;
 }
 
-uint32_t Layer::setTransactionFlags(uint32_t flags) {
-    return mTransactionFlags |= flags;
+void Layer::setTransactionFlags(uint32_t mask) {
+    mTransactionFlags |= mask;
 }
 
 bool Layer::setPosition(float x, float y) {
@@ -903,7 +896,7 @@
         uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect;
         std::string name = mName + "BackgroundColorLayer";
         mDrawingState.bgColorLayer = mFlinger->getFactory().createEffectLayer(
-                LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), 0, 0, flags,
+                LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), flags,
                                   LayerMetadata()));
 
         // add to child list
@@ -950,7 +943,6 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
-
 bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
         bool allowNonRectPreservingTransforms) {
     ui::Transform t;
@@ -1016,7 +1008,7 @@
     return true;
 }
 
-bool Layer::setLayerStack(uint32_t layerStack) {
+bool Layer::setLayerStack(ui::LayerStack layerStack) {
     if (mDrawingState.layerStack == layerStack) return false;
     mDrawingState.sequence++;
     mDrawingState.layerStack = layerStack;
@@ -1063,12 +1055,11 @@
     return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
 };
 
-uint32_t Layer::getLayerStack() const {
-    auto p = mDrawingParent.promote();
-    if (p == nullptr) {
-        return getDrawingState().layerStack;
+ui::LayerStack Layer::getLayerStack() const {
+    if (const auto parent = mDrawingParent.promote()) {
+        return parent->getLayerStack();
     }
-    return p->getLayerStack();
+    return getDrawingState().layerStack;
 }
 
 bool Layer::setShadowRadius(float shadowRadius) {
@@ -1149,7 +1140,7 @@
     if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
         childrenHaveFrameRate) {
         *transactionNeeded |=
-                setFrameRateForLayerTree(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
+                setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote));
     }
 
     // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
@@ -1183,9 +1174,6 @@
 }
 
 bool Layer::setFrameRate(FrameRate frameRate) {
-    if (!mFlinger->useFrameRateApi) {
-        return false;
-    }
     if (mDrawingState.frameRate == frameRate) {
         return false;
     }
@@ -1275,6 +1263,7 @@
                                                                  getSequence(), mName,
                                                                  mTransactionName,
                                                                  /*isBuffer*/ false, getGameMode());
+    surfaceFrame->setActualStartTime(info.startTimeNanos);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
     surfaceFrame->setAcquireFenceTime(postTime);
@@ -1292,6 +1281,7 @@
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName, debugName,
                                                                  /*isBuffer*/ true, getGameMode());
+    surfaceFrame->setActualStartTime(info.startTimeNanos);
     // For buffers, acquire fence time will set during latch.
     surfaceFrame->setActualQueueTime(queueTime);
     const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -1316,8 +1306,8 @@
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
-    mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
-                                             LayerHistory::LayerUpdateType::SetFrameRate);
+    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+    mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate);
 
     return true;
 }
@@ -1382,7 +1372,7 @@
 
     info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
-    info.mLayerStack = getLayerStack();
+    info.mLayerStack = getLayerStack().id;
     info.mX = ds.transform.tx();
     info.mY = ds.transform.ty();
     info.mZ = ds.z;
@@ -1434,19 +1424,6 @@
     result.append("\n");
 }
 
-std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) {
-    switch (compatibility) {
-        case FrameRateCompatibility::Default:
-            return "Default";
-        case FrameRateCompatibility::ExactOrMultiple:
-            return "ExactOrMultiple";
-        case FrameRateCompatibility::NoVote:
-            return "NoVote";
-        case FrameRateCompatibility::Exact:
-            return "Exact";
-    }
-}
-
 void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
     const auto outputLayer = findOutputLayerForDisplay(&display);
     if (!outputLayer) {
@@ -1485,8 +1462,8 @@
     const auto frameRate = getFrameRateForLayerTree();
     if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
         StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
-                      frameRateCompatibilityString(frameRate.type).c_str(),
-                      toString(frameRate.seamlessness).c_str());
+                      ftl::enum_string(frameRate.type).c_str(),
+                      ftl::enum_string(frameRate.seamlessness).c_str());
     } else {
         result.append(41, ' ');
     }
@@ -1569,11 +1546,10 @@
     return count;
 }
 
-void Layer::setGameModeForTree(int parentGameMode) {
-    int gameMode = parentGameMode;
-    auto& currentState = getDrawingState();
+void Layer::setGameModeForTree(GameMode gameMode) {
+    const auto& currentState = getDrawingState();
     if (currentState.metadata.has(METADATA_GAME_MODE)) {
-        gameMode = currentState.metadata.getInt32(METADATA_GAME_MODE, 0);
+        gameMode = static_cast<GameMode>(currentState.metadata.getInt32(METADATA_GAME_MODE, 0));
     }
     setGameMode(gameMode);
     for (const sp<Layer>& child : mCurrentChildren) {
@@ -1599,7 +1575,7 @@
     const auto removeResult = mCurrentChildren.remove(layer);
 
     updateTreeHasFrameRateVote();
-    layer->setGameModeForTree(0);
+    layer->setGameModeForTree(GameMode::Unsupported);
     layer->updateTreeHasFrameRateVote();
 
     return removeResult;
@@ -2028,10 +2004,10 @@
     writeToProtoDrawingState(layerProto, traceFlags, display);
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
 
-    if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
+    if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
         // Only populate for the primary display.
         if (display) {
-            const Hwc2::IComposerClient::Composition compositionType = getCompositionType(*display);
+            const auto compositionType = getCompositionType(*display);
             layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
         }
     }
@@ -2046,43 +2022,38 @@
 void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
                                      const DisplayDevice* display) {
     const ui::Transform transform = getTransform();
+    auto buffer = getBuffer();
+    if (buffer != nullptr) {
+        LayerProtoHelper::writeToProto(buffer,
+                                       [&]() { return layerInfo->mutable_active_buffer(); });
+        LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
+                                                 layerInfo->mutable_buffer_transform());
+    }
+    layerInfo->set_invalidate(contentDirty);
+    layerInfo->set_is_protected(isProtected());
+    layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
+    layerInfo->set_queued_frames(getQueuedFrameCount());
+    layerInfo->set_refresh_pending(isBufferLatched());
+    layerInfo->set_curr_frame(mCurrentFrameNumber);
+    layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
-    if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
+    layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
+    layerInfo->set_corner_radius(getRoundedCornerState().radius);
+    layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
+    layerInfo->set_is_trusted_overlay(isTrustedOverlay());
+    LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
+    LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+                                           [&]() { return layerInfo->mutable_position(); });
+    LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
+    if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
+        LayerProtoHelper::writeToProto(getVisibleRegion(display),
+                                       [&]() { return layerInfo->mutable_visible_region(); });
+    }
+    LayerProtoHelper::writeToProto(surfaceDamageRegion,
+                                   [&]() { return layerInfo->mutable_damage_region(); });
 
-        auto buffer = getBuffer();
-        if (buffer != nullptr) {
-            LayerProtoHelper::writeToProto(buffer,
-                                           [&]() { return layerInfo->mutable_active_buffer(); });
-            LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
-                                                     layerInfo->mutable_buffer_transform());
-        }
-        layerInfo->set_invalidate(contentDirty);
-        layerInfo->set_is_protected(isProtected());
-        layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
-        layerInfo->set_queued_frames(getQueuedFrameCount());
-        layerInfo->set_refresh_pending(isBufferLatched());
-        layerInfo->set_curr_frame(mCurrentFrameNumber);
-        layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
-
-        layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
-        layerInfo->set_corner_radius(getRoundedCornerState().radius);
-        layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
-        layerInfo->set_is_trusted_overlay(isTrustedOverlay());
-        LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
-        LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
-                                               [&]() { return layerInfo->mutable_position(); });
-        LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
-        if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
-            LayerProtoHelper::writeToProto(getVisibleRegion(display),
-                                           [&]() { return layerInfo->mutable_visible_region(); });
-        }
-        LayerProtoHelper::writeToProto(surfaceDamageRegion,
-                                       [&]() { return layerInfo->mutable_damage_region(); });
-
-        if (hasColorTransform()) {
-            LayerProtoHelper::writeToProto(getColorTransform(),
-                                           layerInfo->mutable_color_transform());
-        }
+    if (hasColorTransform()) {
+        LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform());
     }
 
     LayerProtoHelper::writeToProto(mSourceBounds,
@@ -2102,73 +2073,69 @@
 
     ui::Transform requestedTransform = state.transform;
 
-    if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
-        layerInfo->set_id(sequence);
-        layerInfo->set_name(getName().c_str());
-        layerInfo->set_type(getType());
+    layerInfo->set_id(sequence);
+    layerInfo->set_name(getName().c_str());
+    layerInfo->set_type(getType());
 
-        for (const auto& child : children) {
-            layerInfo->add_children(child->sequence);
-        }
-
-        for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
-            sp<Layer> strongRelative = weakRelative.promote();
-            if (strongRelative != nullptr) {
-                layerInfo->add_relatives(strongRelative->sequence);
-            }
-        }
-
-        LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
-                                       [&]() { return layerInfo->mutable_transparent_region(); });
-
-        layerInfo->set_layer_stack(getLayerStack());
-        layerInfo->set_z(state.z);
-
-        LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(),
-                                               [&]() {
-                                                   return layerInfo->mutable_requested_position();
-                                               });
-
-        LayerProtoHelper::writeSizeToProto(state.width, state.height,
-                                           [&]() { return layerInfo->mutable_size(); });
-
-        LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
-
-        layerInfo->set_is_opaque(isOpaque(state));
-
-
-        layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
-        LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
-        LayerProtoHelper::writeToProto(state.color,
-                                       [&]() { return layerInfo->mutable_requested_color(); });
-        layerInfo->set_flags(state.flags);
-
-        LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
-                                                 layerInfo->mutable_requested_transform());
-
-        auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
-        if (parent != nullptr) {
-            layerInfo->set_parent(parent->sequence);
-        } else {
-            layerInfo->set_parent(-1);
-        }
-
-        auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
-        if (zOrderRelativeOf != nullptr) {
-            layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
-        } else {
-            layerInfo->set_z_order_relative_of(-1);
-        }
-
-        layerInfo->set_is_relative_of(state.isRelativeOf);
-
-        layerInfo->set_owner_uid(mOwnerUid);
+    for (const auto& child : children) {
+        layerInfo->add_children(child->sequence);
     }
 
-    if (traceFlags & SurfaceTracing::TRACE_INPUT) {
+    for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
+        sp<Layer> strongRelative = weakRelative.promote();
+        if (strongRelative != nullptr) {
+            layerInfo->add_relatives(strongRelative->sequence);
+        }
+    }
+
+    LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
+                                   [&]() { return layerInfo->mutable_transparent_region(); });
+
+    layerInfo->set_layer_stack(getLayerStack().id);
+    layerInfo->set_z(state.z);
+
+    LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+        return layerInfo->mutable_requested_position();
+    });
+
+    LayerProtoHelper::writeSizeToProto(state.width, state.height,
+                                       [&]() { return layerInfo->mutable_size(); });
+
+    LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
+
+    layerInfo->set_is_opaque(isOpaque(state));
+
+    layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
+    LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
+    LayerProtoHelper::writeToProto(state.color,
+                                   [&]() { return layerInfo->mutable_requested_color(); });
+    layerInfo->set_flags(state.flags);
+
+    LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+                                             layerInfo->mutable_requested_transform());
+
+    auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
+    if (parent != nullptr) {
+        layerInfo->set_parent(parent->sequence);
+    } else {
+        layerInfo->set_parent(-1);
+    }
+
+    auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
+    if (zOrderRelativeOf != nullptr) {
+        layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
+    } else {
+        layerInfo->set_z_order_relative_of(-1);
+    }
+
+    layerInfo->set_is_relative_of(state.isRelativeOf);
+
+    layerInfo->set_owner_uid(mOwnerUid);
+
+    if (traceFlags & LayerTracing::TRACE_INPUT) {
         WindowInfo info;
         if (useDrawing) {
-            info = fillInputInfo({nullptr});
+            info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
         } else {
             info = state.inputInfo;
         }
@@ -2177,7 +2144,7 @@
                                        [&]() { return layerInfo->mutable_input_window_info(); });
     }
 
-    if (traceFlags & SurfaceTracing::TRACE_EXTRA) {
+    if (traceFlags & LayerTracing::TRACE_EXTRA) {
         auto protoMap = layerInfo->mutable_metadata();
         for (const auto& entry : state.metadata.mMap) {
             (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
@@ -2197,7 +2164,7 @@
     return getCroppedBufferSize(getDrawingState());
 }
 
-void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotatedDisplay) {
+void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) {
     // Transform layer size to screen space and inset it by surface insets.
     // If this is a portal window, set the touchableRegion to the layerBounds.
     Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
@@ -2219,13 +2186,13 @@
         return;
     }
 
-    ui::Transform layerToDisplay = getInputTransform();
+    const ui::Transform layerTransform = getInputTransform();
     // Transform that takes window coordinates to non-rotated display coordinates
-    ui::Transform t = toNonRotatedDisplay * layerToDisplay;
+    ui::Transform t = displayTransform * layerTransform;
     int32_t xSurfaceInset = info.surfaceInset;
     int32_t ySurfaceInset = info.surfaceInset;
-    // Bring screenBounds into non-rotated space
-    Rect screenBounds = toNonRotatedDisplay.transform(Rect{mScreenBounds});
+    // Bring screenBounds into non-unrotated space
+    Rect screenBounds = displayTransform.transform(Rect{mScreenBounds});
 
     const float xScale = t.getScaleX();
     const float yScale = t.getScaleY();
@@ -2372,47 +2339,21 @@
     }
 }
 
-WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
+WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) {
     if (!hasInputInfo()) {
         mDrawingState.inputInfo.name = getName();
         mDrawingState.inputInfo.ownerUid = mOwnerUid;
         mDrawingState.inputInfo.ownerPid = mOwnerPid;
         mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
         mDrawingState.inputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL;
-        mDrawingState.inputInfo.displayId = getLayerStack();
+        mDrawingState.inputInfo.displayId = getLayerStack().id;
     }
 
     WindowInfo info = mDrawingState.inputInfo;
     info.id = sequence;
-    info.displayId = getLayerStack();
+    info.displayId = getLayerStack().id;
 
-    // Transform that goes from "logical(rotated)" display to the non-rotated display.
-    ui::Transform toNonRotatedDisplay;
-    if (display) {
-        // The physical orientation is set when the orientation of the display panel is different
-        // than the default orientation of the device. We do not need to expose the physical
-        // orientation of the panel outside of SurfaceFlinger.
-        const ui::Rotation inversePhysicalOrientation =
-                ui::ROTATION_0 - display->getPhysicalOrientation();
-        auto width = display->getWidth();
-        auto height = display->getHeight();
-        if (inversePhysicalOrientation == ui::ROTATION_90 ||
-            inversePhysicalOrientation == ui::ROTATION_270) {
-            std::swap(width, height);
-        }
-        const auto rotationFlags = ui::Transform::toRotationFlags(inversePhysicalOrientation);
-        const ui::Transform undoPhysicalOrientation(rotationFlags, width, height);
-        toNonRotatedDisplay = undoPhysicalOrientation * display->getTransform();
-
-        // Send the inverse of the display orientation so that input can transform points back to
-        // the rotated display space.
-        const ui::Rotation inverseOrientation = ui::ROTATION_0 - display->getOrientation();
-        info.displayOrientation = ui::Transform::toRotationFlags(inverseOrientation);
-
-        info.displayWidth = width;
-        info.displayHeight = height;
-    }
-    fillInputFrameInfo(info, toNonRotatedDisplay);
+    fillInputFrameInfo(info, displayTransform);
 
     // For compatibility reasons we let layers which can receive input
     // receive input before they have actually submitted a buffer. Because
@@ -2427,17 +2368,19 @@
     fillTouchOcclusionMode(info);
     handleDropInputMode(info);
 
+    // If the window will be blacked out on a display because the display does not have the secure
+    // flag and the layer has the secure flag set, then drop input.
+    if (!displayIsSecure && isSecure()) {
+        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+    }
+
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
-        if (cropLayer == nullptr) {
-            info.touchableRegion = Region(toNonRotatedDisplay.transform(Rect{mScreenBounds}));
-        } else {
-            info.touchableRegion =
-                    Region(toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
-        }
+        const Rect bounds(cropLayer ? cropLayer->mScreenBounds : mScreenBounds);
+        info.touchableRegion = Region(displayTransform.transform(bounds));
     } else if (cropLayer != nullptr) {
         info.touchableRegion = info.touchableRegion.intersect(
-                toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
+                displayTransform.transform(Rect{cropLayer->mScreenBounds}));
     }
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -2448,9 +2391,8 @@
     // If the layer is a clone, we need to crop the input region to cloned root to prevent
     // touches from going outside the cloned area.
     if (isClone()) {
-        sp<Layer> clonedRoot = getClonedRoot();
-        if (clonedRoot != nullptr) {
-            Rect rect = toNonRotatedDisplay.transform(Rect{clonedRoot->mScreenBounds});
+        if (const sp<Layer> clonedRoot = getClonedRoot()) {
+            const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds});
             info.touchableRegion = info.touchableRegion.intersect(rect);
         }
     }
@@ -2645,14 +2587,14 @@
     }
 }
 
-bool Layer::getPrimaryDisplayOnly() const {
+bool Layer::isInternalDisplayOverlay() const {
     const State& s(mDrawingState);
     if (s.flags & layer_state_t::eLayerSkipScreenshot) {
         return true;
     }
 
     sp<Layer> parent = mDrawingParent.promote();
-    return parent == nullptr ? false : parent->getPrimaryDisplayOnly();
+    return parent && parent->isInternalDisplayOverlay();
 }
 
 void Layer::setClonedChild(const sp<Layer>& clonedChild) {
@@ -2696,12 +2638,11 @@
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
-    return stream << "{rate=" << rate.rate
-                  << " type=" << Layer::frameRateCompatibilityString(rate.type)
-                  << " seamlessness=" << toString(rate.seamlessness) << "}";
+    return stream << "{rate=" << rate.rate << " type=" << ftl::enum_string(rate.type)
+                  << " seamlessness=" << ftl::enum_string(rate.seamlessness) << '}';
 }
 
-}; // namespace android
+} // namespace android
 
 #if defined(__gl_h_)
 #error "don't include gl/gl.h in this file"
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c1af468..31cdf0b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -18,7 +18,6 @@
 #pragma once
 
 #include <android/gui/DropInputMode.h>
-#include <compositionengine/LayerFE.h>
 #include <gui/BufferQueue.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
@@ -39,6 +38,10 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
+#include <compositionengine/LayerFE.h>
+#include <scheduler/Fps.h>
+#include <scheduler/Seamlessness.h>
+
 #include <chrono>
 #include <cstdint>
 #include <list>
@@ -49,15 +52,13 @@
 #include "ClientCache.h"
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/HWComposer.h"
-#include "Fps.h"
 #include "FrameTracker.h"
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "RenderArea.h"
 #include "Scheduler/LayerInfo.h"
-#include "Scheduler/Seamlessness.h"
 #include "SurfaceFlinger.h"
-#include "SurfaceTracing.h"
+#include "Tracing/LayerTracing.h"
 #include "TransactionCallbackInvoker.h"
 
 using namespace android::surfaceflinger;
@@ -85,20 +86,18 @@
 } // namespace frametimeline
 
 struct LayerCreationArgs {
-    LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
-                      uint32_t flags, LayerMetadata);
+    LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata);
 
     SurfaceFlinger* flinger;
     const sp<Client> client;
     std::string name;
-    uint32_t w;
-    uint32_t h;
     uint32_t flags;
     LayerMetadata metadata;
 
     pid_t callingPid;
     uid_t callingUid;
     uint32_t textureName;
+    std::optional<uint32_t> sequence = std::nullopt;
 };
 
 class Layer : public virtual RefBase, compositionengine::LayerFE {
@@ -152,12 +151,7 @@
         Geometry requested_legacy;
         int32_t z;
 
-        // The identifier of the layer stack this layer belongs to. A layer can
-        // only be associated to a single layer stack. A layer stack is a
-        // z-ordered group of layers which can be associated to one or more
-        // displays. Using the same layer stack on different displays is a way
-        // to achieve mirroring.
-        uint32_t layerStack;
+        ui::LayerStack layerStack;
 
         uint32_t flags;
         uint8_t reserved[2];
@@ -285,6 +279,8 @@
         sp<IBinder> releaseBufferEndpoint;
 
         gui::DropInputMode dropInputMode;
+
+        bool autoRefresh = false;
     };
 
     /*
@@ -332,7 +328,6 @@
 
     static bool isLayerFocusedBasedOnPriority(int32_t priority);
     static void miniDumpHeader(std::string& result);
-    static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
 
     // Provide unique string for each class type in the Layer hierarchy
     virtual const char* getType() const = 0;
@@ -408,8 +403,8 @@
     virtual bool setTransparentRegionHint(const Region& transparent);
     virtual bool setTrustedOverlay(bool);
     virtual bool setFlags(uint32_t flags, uint32_t mask);
-    virtual bool setLayerStack(uint32_t layerStack);
-    virtual uint32_t getLayerStack() const;
+    virtual bool setLayerStack(ui::LayerStack);
+    virtual ui::LayerStack getLayerStack() const;
     virtual bool setMetadata(const LayerMetadata& data);
     virtual void setChildrenDrawingParent(const sp<Layer>&);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -421,17 +416,11 @@
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& /*buffer*/,
-                           const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/,
-                           nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
-                           const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */,
-                           std::optional<nsecs_t> /* dequeueTime */,
-                           const FrameTimelineInfo& /*info*/,
-                           const sp<ITransactionCompletedListener>& /* releaseBufferListener */,
-                           const sp<IBinder>& /* releaseBufferEndpoint */) {
+    virtual bool setBuffer(const BufferData&, nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
+                           bool /*isAutoTimestamp*/, std::optional<nsecs_t> /* dequeueTime */,
+                           const FrameTimelineInfo& /*info*/) {
         return false;
     };
-    virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
     virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
     virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
     virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
@@ -532,18 +521,14 @@
 
     virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
 
-    virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; }
-
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(const DisplayDevice*,
+    virtual void onPostComposition(const DisplayDevice*,
                                    const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming&) {
-        return false;
-    }
+                                   const CompositorTiming&) {}
 
     // If a buffer was replaced this frame, release the former buffer
     virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
@@ -604,10 +589,6 @@
     }
     virtual FrameRate getFrameRateForLayerTree() const;
 
-    virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
-        return {};
-    }
-
     virtual bool getTransformToDisplayInverse() const { return false; }
 
     // Returns how rounded corners should be drawn for this layer.
@@ -635,7 +616,13 @@
     void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
     std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
             compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
-    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+    void onLayerDisplayed(
+            std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) override;
+
+    void setWasClientComposed(const sp<Fence>& fence) override {
+        mLastClientCompositionFence = fence;
+    }
+
     const char* getDebugName() const override;
 
     bool setShadowRadius(float shadowRadius);
@@ -648,13 +635,12 @@
     bool isLegacyDataSpace() const;
 
     uint32_t getTransactionFlags() const { return mTransactionFlags; }
-    uint32_t getTransactionFlags(uint32_t flags);
-    uint32_t setTransactionFlags(uint32_t flags);
 
-    // Deprecated, please use compositionengine::Output::belongsInOutput()
-    // instead.
-    // TODO(lpique): Move the remaining callers (screencap) to the new function.
-    bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
+    // Sets the masked bits.
+    void setTransactionFlags(uint32_t mask);
+
+    // Clears and returns the masked bits.
+    uint32_t clearTransactionFlags(uint32_t mask);
 
     FloatRect getBounds(const Region& activeTransparentRegion) const;
     FloatRect getBounds() const;
@@ -687,6 +673,14 @@
      */
     bool isHiddenByPolicy() const;
 
+    // True if the layer should be skipped in screenshots, screen recordings,
+    // and mirroring to external or virtual displays.
+    bool isInternalDisplayOverlay() const;
+
+    ui::LayerFilter getOutputFilter() const {
+        return {getLayerStack(), isInternalDisplayOverlay()};
+    }
+
     bool isRemovedFromCurrentState() const;
 
     LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
@@ -699,12 +693,10 @@
     // external mStateLock. If writing drawing state, this function should be called on the
     // main or tracing thread.
     void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
-                                 uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
+                                 uint32_t traceFlags = LayerTracing::TRACE_ALL);
 
     gui::WindowInfo::Type getWindowType() const { return mWindowType; }
 
-    bool getPrimaryDisplayOnly() const;
-
     void updateMirrorInfo();
 
     /*
@@ -854,18 +846,19 @@
     bool getPremultipledAlpha() const;
     void setInputInfo(const gui::WindowInfo& info);
 
-    gui::WindowInfo fillInputInfo(const sp<DisplayDevice>& display);
+    gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure);
+
     /**
      * Returns whether this layer has an explicitly set input-info.
      */
     bool hasInputInfo() const;
 
-    // Sets the parent's gameMode for this layer and all its children. Parent's gameMode is applied
-    // only to layers that do not have the GAME_MODE_METADATA set by WMShell. Any layer(along with
-    // its children) that has the metadata set will use the gameMode from the metadata.
-    void setGameModeForTree(int32_t parentGameMode);
-    void setGameMode(int32_t gameMode) { mGameMode = gameMode; };
-    int32_t getGameMode() const { return mGameMode; }
+    // Sets the GameMode for the tree rooted at this layer. A layer in the tree inherits this
+    // GameMode unless it (or an ancestor) has GAME_MODE_METADATA.
+    void setGameModeForTree(GameMode);
+
+    void setGameMode(GameMode gameMode) { mGameMode = gameMode; }
+    GameMode getGameMode() const { return mGameMode; }
 
     virtual uid_t getOwnerUid() const { return mOwnerUid; }
 
@@ -886,7 +879,7 @@
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
     // the same.
-    int32_t sequence{sSequence++};
+    const int32_t sequence;
 
     bool mPendingHWCDestroy{false};
 
@@ -1036,13 +1029,16 @@
 
     mutable bool mDrawingStateModified = false;
 
+    sp<Fence> mLastClientCompositionFence;
+    bool mLastClientCompositionDisplayed = false;
 private:
     virtual void setTransformHint(ui::Transform::RotationFlags) {}
 
     // Returns true if the layer can draw shadows on its border.
     virtual bool canDrawShadows() const { return true; }
 
-    Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
+    aidl::android::hardware::graphics::composer3::Composition getCompositionType(
+            const DisplayDevice&) const;
 
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
@@ -1078,8 +1074,8 @@
     // hasInputInfo() or no-op if no such parent is found.
     void fillTouchOcclusionMode(gui::WindowInfo& info);
 
-    // Fills in the frame and transform info for the gui::WindowInfo
-    void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toNonRotatedDisplay);
+    // Fills in the frame and transform info for the gui::WindowInfo.
+    void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform);
 
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
@@ -1115,14 +1111,15 @@
     // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
     float mEffectiveShadowRadius = 0.f;
 
-    // Game mode for the layer. Set by WindowManagerShell, game mode is used in
-    // metrics(SurfaceFlingerStats).
-    int32_t mGameMode = 0;
+    // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
+    GameMode mGameMode = GameMode::Unsupported;
 
     // A list of regions on this layer that should have blurs.
     const std::vector<BlurRegion> getBlurRegions() const;
 
     bool mIsAtRoot = false;
+
+    uint32_t mLayerCreationFlags;
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 1062126..ee23561 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -53,28 +53,52 @@
         return;
     }
 
+    writeToProto(region, getRegionProto());
+}
+
+void LayerProtoHelper::writeToProto(const Region& region, RegionProto* regionProto) {
+    if (region.isEmpty()) {
+        return;
+    }
+
     Region::const_iterator head = region.begin();
     Region::const_iterator const tail = region.end();
     // Use a lambda do avoid writing the object header when the object is empty
-    RegionProto* regionProto = getRegionProto();
     while (head != tail) {
-        std::function<RectProto*()> getProtoRect = [&]() { return regionProto->add_rect(); };
-        writeToProto(*head, getProtoRect);
+        writeToProto(*head, regionProto->add_rect());
         head++;
     }
 }
 
+void LayerProtoHelper::readFromProto(const RegionProto& regionProto, Region& outRegion) {
+    for (int i = 0; i < regionProto.rect_size(); i++) {
+        Rect rect;
+        readFromProto(regionProto.rect(i), rect);
+        outRegion.orSelf(rect);
+    }
+}
+
 void LayerProtoHelper::writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto) {
     if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
         // Use a lambda do avoid writing the object header when the object is empty
-        RectProto* rectProto = getRectProto();
-        rectProto->set_left(rect.left);
-        rectProto->set_top(rect.top);
-        rectProto->set_bottom(rect.bottom);
-        rectProto->set_right(rect.right);
+        writeToProto(rect, getRectProto());
     }
 }
 
+void LayerProtoHelper::writeToProto(const Rect& rect, RectProto* rectProto) {
+    rectProto->set_left(rect.left);
+    rectProto->set_top(rect.top);
+    rectProto->set_bottom(rect.bottom);
+    rectProto->set_right(rect.right);
+}
+
+void LayerProtoHelper::readFromProto(const RectProto& proto, Rect& outRect) {
+    outRect.left = proto.left();
+    outRect.top = proto.top();
+    outRect.bottom = proto.bottom();
+    outRect.right = proto.right();
+}
+
 void LayerProtoHelper::writeToProto(const FloatRect& rect,
                                     std::function<FloatRectProto*()> getFloatRectProto) {
     if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
@@ -189,6 +213,39 @@
     }
 }
 
+void LayerProtoHelper::readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix) {
+    for (int i = 0; i < mat4::ROW_SIZE; i++) {
+        for (int j = 0; j < mat4::COL_SIZE; j++) {
+            matrix[i][j] = colorTransformProto.val(i * mat4::COL_SIZE + j);
+        }
+    }
+}
+
+void LayerProtoHelper::writeToProto(const android::BlurRegion region, BlurRegion* proto) {
+    proto->set_blur_radius(region.blurRadius);
+    proto->set_corner_radius_tl(region.cornerRadiusTL);
+    proto->set_corner_radius_tr(region.cornerRadiusTR);
+    proto->set_corner_radius_bl(region.cornerRadiusBL);
+    proto->set_corner_radius_br(region.cornerRadiusBR);
+    proto->set_alpha(region.alpha);
+    proto->set_left(region.left);
+    proto->set_top(region.top);
+    proto->set_right(region.right);
+    proto->set_bottom(region.bottom);
+}
+
+void LayerProtoHelper::readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion) {
+    outRegion.blurRadius = proto.blur_radius();
+    outRegion.cornerRadiusTL = proto.corner_radius_tl();
+    outRegion.cornerRadiusTR = proto.corner_radius_tr();
+    outRegion.cornerRadiusBL = proto.corner_radius_bl();
+    outRegion.cornerRadiusBR = proto.corner_radius_br();
+    outRegion.alpha = proto.alpha();
+    outRegion.left = proto.left();
+    outRegion.top = proto.top();
+    outRegion.right = proto.right();
+    outRegion.bottom = proto.bottom();
+}
 } // namespace surfaceflinger
 } // namespace android
 
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 36e0647..249ec42 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -19,6 +19,7 @@
 #include <Layer.h>
 #include <gui/WindowInfo.h>
 #include <math/vec4.h>
+#include <ui/BlurRegion.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -33,9 +34,13 @@
     static void writeSizeToProto(const uint32_t w, const uint32_t h,
                                  std::function<SizeProto*()> getSizeProto);
     static void writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto);
+    static void writeToProto(const Rect& rect, RectProto* rectProto);
+    static void readFromProto(const RectProto& proto, Rect& outRect);
     static void writeToProto(const FloatRect& rect,
                              std::function<FloatRectProto*()> getFloatRectProto);
     static void writeToProto(const Region& region, std::function<RegionProto*()> getRegionProto);
+    static void writeToProto(const Region& region, RegionProto* regionProto);
+    static void readFromProto(const RegionProto& regionProto, Region& outRegion);
     static void writeToProto(const half4 color, std::function<ColorProto*()> getColorProto);
     // This writeToProto for transform is incorrect, but due to backwards compatibility, we can't
     // update Layers to use it. Use writeTransformToProto for any new transform proto data.
@@ -49,6 +54,9 @@
                              const wp<Layer>& touchableRegionBounds,
                              std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
     static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto);
+    static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix);
+    static void writeToProto(const android::BlurRegion region, BlurRegion*);
+    static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion);
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 11fe6d0..a1e1455 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -112,12 +112,10 @@
         }
         drawLayers();
     } else {
-        uint32_t w = static_cast<uint32_t>(getWidth());
-        uint32_t h = static_cast<uint32_t>(getHeight());
         // In the "childrenOnly" case we reparent the children to a screenshot
         // layer which has no properties set and which does not draw.
         sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
-                {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()});
+                {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()});
 
         ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
         drawLayers();
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index aee820a..f52e60d 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -45,8 +45,8 @@
     const auto& lState = l->getDrawingState();
     const auto& rState = r->getDrawingState();
 
-    uint32_t ls = lState.layerStack;
-    uint32_t rs = rState.layerStack;
+    const auto ls = lState.layerStack;
+    const auto rs = rState.layerStack;
     if (ls != rs)
         return (ls > rs) ? 1 : -1;
 
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 6b2d745..df76f50 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -33,13 +33,7 @@
     mFlinger(flinger),
     mLayer(layer) {}
 
-MonitoredProducer::~MonitoredProducer() {
-    // Remove ourselves from SurfaceFlinger's list. We do this asynchronously
-    // because we don't know where this destructor is called from. It could be
-    // called with the mStateLock held, leading to a dead-lock (it actually
-    // happens).
-    mFlinger->removeGraphicBufferProducerAsync(onAsBinder());
-}
+MonitoredProducer::~MonitoredProducer() {}
 
 status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
     return mProducer->requestBuffer(slot, buf);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index bc32a1d..81c1566 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -25,92 +25,77 @@
 #include "Client.h"
 #include "Layer.h"
 
+#include <SkBlendMode.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkSurface.h>
 #include <gui/IProducerListener.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
 
 #undef LOG_TAG
 #define LOG_TAG "RefreshRateOverlay"
 
 namespace android {
 
-void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
-                                                      const sp<GraphicBuffer>& buffer,
-                                                      uint8_t* pixels) {
-    for (int32_t j = r.top; j < r.bottom; j++) {
-        if (j >= buffer->getHeight()) {
-            break;
-        }
-
-        for (int32_t i = r.left; i < r.right; i++) {
-            if (i >= buffer->getWidth()) {
-                break;
-            }
-
-            uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
-            iter[0] = uint8_t(color.r * 255);
-            iter[1] = uint8_t(color.g * 255);
-            iter[2] = uint8_t(color.b * 255);
-            iter[3] = uint8_t(color.a * 255);
-        }
-    }
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
-                                                         const half4& color,
-                                                         const sp<GraphicBuffer>& buffer,
-                                                         uint8_t* pixels) {
-    const Rect rect = [&]() {
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor& color,
+                                                         SkCanvas& canvas) {
+    const SkRect rect = [&]() {
         switch (segment) {
             case Segment::Upper:
-                return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+                return SkRect::MakeLTRB(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
             case Segment::UpperLeft:
-                return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+                return SkRect::MakeLTRB(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
             case Segment::UpperRight:
-                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
-                            DIGIT_HEIGHT / 2);
+                return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
+                                        DIGIT_HEIGHT / 2);
             case Segment::Middle:
-                return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
-                            DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+                return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2,
+                                        left + DIGIT_WIDTH, DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
             case Segment::LowerLeft:
-                return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+                return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
             case Segment::LowerRight:
-                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
-                            DIGIT_HEIGHT);
-            case Segment::Buttom:
-                return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+                return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2,
+                                        left + DIGIT_WIDTH, DIGIT_HEIGHT);
+            case Segment::Bottom:
+                return SkRect::MakeLTRB(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH,
+                                        DIGIT_HEIGHT);
         }
     }();
 
-    drawRect(rect, color, buffer, pixels);
+    SkPaint paint;
+    paint.setColor(color);
+    paint.setBlendMode(SkBlendMode::kSrc);
+    canvas.drawRect(rect, paint);
 }
 
-void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
-                                                       const sp<GraphicBuffer>& buffer,
-                                                       uint8_t* pixels) {
+void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor& color,
+                                                       SkCanvas& canvas) {
     if (digit < 0 || digit > 9) return;
 
     if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
         digit == 8 || digit == 9)
-        drawSegment(Segment::Upper, left, color, buffer, pixels);
+        drawSegment(Segment::Upper, left, color, canvas);
     if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
-        drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+        drawSegment(Segment::UpperLeft, left, color, canvas);
     if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
         digit == 8 || digit == 9)
-        drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+        drawSegment(Segment::UpperRight, left, color, canvas);
     if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
         digit == 9)
-        drawSegment(Segment::Middle, left, color, buffer, pixels);
+        drawSegment(Segment::Middle, left, color, canvas);
     if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
-        drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+        drawSegment(Segment::LowerLeft, left, color, canvas);
     if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
         digit == 7 || digit == 8 || digit == 9)
-        drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+        drawSegment(Segment::LowerRight, left, color, canvas);
     if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
         digit == 9)
-        drawSegment(Segment::Buttom, left, color, buffer, pixels);
+        drawSegment(Segment::Bottom, left, color, canvas);
 }
 
-std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
-        int number, const half4& color, bool showSpinner) {
+std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::draw(
+        int number, SkColor& color, ui::Transform::RotationFlags rotation, bool showSpinner) {
     if (number < 0 || number > 1000) return {};
 
     const auto hundreds = number / 100;
@@ -120,55 +105,76 @@
     std::vector<sp<GraphicBuffer>> buffers;
     const auto loopCount = showSpinner ? 6 : 1;
     for (int i = 0; i < loopCount; i++) {
+        // Pre-rotate the buffer before it reaches SurfaceFlinger.
+        SkMatrix canvasTransform = SkMatrix();
+        auto [bufferWidth, bufferHeight] = [&] {
+            switch (rotation) {
+                case ui::Transform::ROT_90:
+                    canvasTransform.setTranslate(BUFFER_HEIGHT, 0);
+                    canvasTransform.preRotate(90);
+                    return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH);
+                case ui::Transform::ROT_270:
+                    canvasTransform.setRotate(270, BUFFER_WIDTH / 2.0, BUFFER_WIDTH / 2.0);
+                    return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH);
+                default:
+                    return std::make_tuple(BUFFER_WIDTH, BUFFER_HEIGHT);
+            }
+        }();
         sp<GraphicBuffer> buffer =
-                new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                new GraphicBuffer(bufferWidth, bufferHeight, HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                   GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
                                           GRALLOC_USAGE_HW_TEXTURE,
                                   "RefreshRateOverlayBuffer");
         const status_t bufferStatus = buffer->initCheck();
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
                             bufferStatus);
-        uint8_t* pixels;
-        buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
-        // Clear buffer content
-        drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+
+        sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight);
+        SkCanvas* canvas = surface->getCanvas();
+        canvas->setMatrix(canvasTransform);
+
         int left = 0;
         if (hundreds != 0) {
-            drawDigit(hundreds, left, color, buffer, pixels);
+            drawDigit(hundreds, left, color, *canvas);
         }
         left += DIGIT_WIDTH + DIGIT_SPACE;
 
         if (tens != 0) {
-            drawDigit(tens, left, color, buffer, pixels);
+            drawDigit(tens, left, color, *canvas);
         }
         left += DIGIT_WIDTH + DIGIT_SPACE;
 
-        drawDigit(ones, left, color, buffer, pixels);
+        drawDigit(ones, left, color, *canvas);
         left += DIGIT_WIDTH + DIGIT_SPACE;
 
         if (showSpinner) {
             switch (i) {
                 case 0:
-                    drawSegment(Segment::Upper, left, color, buffer, pixels);
+                    drawSegment(Segment::Upper, left, color, *canvas);
                     break;
                 case 1:
-                    drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+                    drawSegment(Segment::UpperRight, left, color, *canvas);
                     break;
                 case 2:
-                    drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+                    drawSegment(Segment::LowerRight, left, color, *canvas);
                     break;
                 case 3:
-                    drawSegment(Segment::Buttom, left, color, buffer, pixels);
+                    drawSegment(Segment::Bottom, left, color, *canvas);
                     break;
                 case 4:
-                    drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+                    drawSegment(Segment::LowerLeft, left, color, *canvas);
                     break;
                 case 5:
-                    drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+                    drawSegment(Segment::UpperLeft, left, color, *canvas);
                     break;
             }
         }
 
+        void* pixels = nullptr;
+        buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+        const SkImageInfo& imageInfo = surface->imageInfo();
+        size_t dstRowBytes = buffer->getStride() * imageInfo.bytesPerPixel();
+        canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
         buffer->unlock();
         buffers.emplace_back(buffer);
     }
@@ -186,35 +192,49 @@
 }
 
 bool RefreshRateOverlay::createLayer() {
-    int32_t layerId;
-    const status_t ret =
-            mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
-                                 SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
-                                 PIXEL_FORMAT_RGBA_8888,
-                                 ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
-                                 &mIBinder, &mGbp, nullptr, &layerId);
-    if (ret) {
+    mSurfaceControl =
+            SurfaceComposerClient::getDefault()
+                    ->createSurface(String8("RefreshRateOverlay"), SevenSegmentDrawer::getWidth(),
+                                    SevenSegmentDrawer::getHeight(), PIXEL_FORMAT_RGBA_8888,
+                                    ISurfaceComposerClient::eFXSurfaceBufferState);
+
+    if (!mSurfaceControl) {
         ALOGE("failed to create buffer state layer");
         return false;
     }
 
-    mLayer = mClient->getLayerUser(mIBinder);
-    mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
-    mLayer->setIsAtRoot(true);
-
-    // setting Layer's Z requires resorting layersSortedByZ
-    ssize_t idx = mFlinger.mDrawingState.layersSortedByZ.indexOf(mLayer);
-    if (mLayer->setLayer(INT32_MAX - 2) && idx >= 0) {
-        mFlinger.mDrawingState.layersSortedByZ.removeAt(idx);
-        mFlinger.mDrawingState.layersSortedByZ.add(mLayer);
-    }
+    SurfaceComposerClient::Transaction()
+            .setFrameRate(mSurfaceControl, 0.0f,
+                          static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote),
+                          static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless))
+            .setLayer(mSurfaceControl, INT32_MAX - 2)
+            .setTrustedOverlay(mSurfaceControl, true)
+            .apply();
 
     return true;
 }
 
-const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
-RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
-    if (mBufferCache.find(fps) == mBufferCache.end()) {
+const std::vector<sp<GraphicBuffer>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
+    ui::Transform::RotationFlags transformHint =
+            static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+    // Tell SurfaceFlinger about the pre-rotation on the buffer.
+    const auto transform = [&] {
+        switch (transformHint) {
+            case ui::Transform::ROT_90:
+                return ui::Transform::ROT_270;
+            case ui::Transform::ROT_270:
+                return ui::Transform::ROT_90;
+            default:
+                return ui::Transform::ROT_0;
+        }
+    }();
+
+    SurfaceComposerClient::Transaction t;
+    t.setTransform(mSurfaceControl, transform);
+    t.apply();
+
+    if (mBufferCache.find(transformHint) == mBufferCache.end() ||
+        mBufferCache.at(transformHint).find(fps) == mBufferCache.at(transformHint).end()) {
         // Ensure the range is > 0, so we don't divide by 0.
         const auto rangeLength = std::max(1u, mHighFps - mLowFps);
         // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside
@@ -222,25 +242,18 @@
         fps = std::max(fps, mLowFps);
         fps = std::min(fps, mHighFps);
         const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength;
-        half4 color;
-        color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
-        color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
-        color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
-        color.a = ALPHA;
-        auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
-        std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
-        std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
-                       [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
-                           return std::make_shared<
-                                   renderengine::ExternalTexture>(buffer,
-                                                                  mFlinger.getRenderEngine(),
-                                                                  renderengine::ExternalTexture::
-                                                                          Usage::READABLE);
-                       });
-        mBufferCache.emplace(fps, textures);
+        SkColor4f colorBase = SkColor4f::FromColor(HIGH_FPS_COLOR) * fpsScale;
+        SkColor4f lowFpsColor = SkColor4f::FromColor(LOW_FPS_COLOR) * (1 - fpsScale);
+        colorBase.fR = colorBase.fR + lowFpsColor.fR;
+        colorBase.fG = colorBase.fG + lowFpsColor.fG;
+        colorBase.fB = colorBase.fB + lowFpsColor.fB;
+        colorBase.fA = ALPHA;
+        SkColor color = colorBase.toSkColor();
+        auto buffers = SevenSegmentDrawer::draw(fps, color, transformHint, mShowSpinner);
+        mBufferCache[transformHint].emplace(fps, buffers);
     }
 
-    return mBufferCache[fps];
+    return mBufferCache[transformHint][fps];
 }
 
 void RefreshRateOverlay::setViewport(ui::Size viewport) {
@@ -250,44 +263,37 @@
     Rect frame((3 * width) >> 4, height >> 5);
     frame.offsetBy(width >> 5, height >> 4);
 
-    layer_state_t::matrix22_t matrix;
-    matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
-    matrix.dtdx = 0;
-    matrix.dtdy = 0;
-    matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
-    mLayer->setMatrix(matrix, true);
-    mLayer->setPosition(frame.left, frame.top);
-    mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+    SurfaceComposerClient::Transaction t;
+    t.setMatrix(mSurfaceControl,
+                frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth()), 0, 0,
+                frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight()));
+    t.setPosition(mSurfaceControl, frame.left, frame.top);
+    t.apply();
 }
 
-void RefreshRateOverlay::setLayerStack(uint32_t stack) {
-    mLayer->setLayerStack(stack);
-    mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
+    SurfaceComposerClient::Transaction t;
+    t.setLayerStack(mSurfaceControl, stack);
+    t.apply();
 }
 
-void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
+void RefreshRateOverlay::changeRefreshRate(Fps fps) {
     mCurrentFps = fps.getIntValue();
     auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
-                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
-                      std::nullopt /* dequeueTime */, FrameTimelineInfo{},
-                      nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
-    mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+    SurfaceComposerClient::Transaction t;
+    t.setBuffer(mSurfaceControl, buffer);
+    t.apply();
 }
 
-void RefreshRateOverlay::onInvalidate() {
+void RefreshRateOverlay::animate() {
     if (!mCurrentFps.has_value()) return;
 
     const auto& buffers = getOrCreateBuffers(*mCurrentFps);
     mFrame = (mFrame + 1) % buffers.size();
     auto buffer = buffers[mFrame];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
-                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
-                      std::nullopt /* dequeueTime */, FrameTimelineInfo{},
-                      nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
-    mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+    SurfaceComposerClient::Transaction t;
+    t.setBuffer(mSurfaceControl, buffer);
+    t.apply();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index f9baa89..381df37 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,15 +16,18 @@
 
 #pragma once
 
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <unordered_map>
+
 #include <math/vec4.h>
 #include <renderengine/RenderEngine.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Size.h>
 #include <utils/StrongPointer.h>
 
-#include <unordered_map>
-
-#include "Fps.h"
+#include <scheduler/Fps.h>
 
 namespace android {
 
@@ -34,33 +37,30 @@
 class IGraphicBufferProducer;
 class Layer;
 class SurfaceFlinger;
+class SurfaceControl;
 
 class RefreshRateOverlay {
 public:
     RefreshRateOverlay(SurfaceFlinger&, uint32_t lowFps, uint32_t highFps, bool showSpinner);
 
-    void setLayerStack(uint32_t stack);
+    void setLayerStack(ui::LayerStack);
     void setViewport(ui::Size);
-    void changeRefreshRate(const Fps&);
-    void onInvalidate();
+    void changeRefreshRate(Fps);
+    void animate();
 
 private:
     class SevenSegmentDrawer {
     public:
-        static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color,
-                                                         bool showSpinner);
+        static std::vector<sp<GraphicBuffer>> draw(int number, SkColor& color,
+                                                   ui::Transform::RotationFlags, bool showSpinner);
         static uint32_t getHeight() { return BUFFER_HEIGHT; }
         static uint32_t getWidth() { return BUFFER_WIDTH; }
 
     private:
-        enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom };
+        enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
 
-        static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer,
-                             uint8_t* pixels);
-        static void drawSegment(Segment segment, int left, const half4& color,
-                                const sp<GraphicBuffer>& buffer, uint8_t* pixels);
-        static void drawDigit(int digit, int left, const half4& color,
-                              const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+        static void drawSegment(Segment segment, int left, SkColor& color, SkCanvas& canvas);
+        static void drawDigit(int digit, int left, SkColor& color, SkCanvas& canvas);
 
         static constexpr uint32_t DIGIT_HEIGHT = 100;
         static constexpr uint32_t DIGIT_WIDTH = 64;
@@ -71,28 +71,30 @@
     };
 
     bool createLayer();
-    const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& getOrCreateBuffers(
-            uint32_t fps);
+
+    const std::vector<sp<GraphicBuffer>>& getOrCreateBuffers(uint32_t fps);
 
     SurfaceFlinger& mFlinger;
     const sp<Client> mClient;
-    sp<Layer> mLayer;
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
 
-    std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>
+    std::unordered_map<ui::Transform::RotationFlags,
+                       std::unordered_map<int, std::vector<sp<GraphicBuffer>>>>
             mBufferCache;
     std::optional<int> mCurrentFps;
     int mFrame = 0;
     static constexpr float ALPHA = 0.8f;
-    const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
-    const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
+    const SkColor LOW_FPS_COLOR = SK_ColorRED;
+    const SkColor HIGH_FPS_COLOR = SK_ColorGREEN;
 
     const bool mShowSpinner;
 
     // Interpolate the colors between these values.
     const uint32_t mLowFps;
     const uint32_t mHighFps;
+
+    sp<SurfaceControl> mSurfaceControl;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index aa2fec5..da8c3e0 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -30,7 +30,6 @@
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <cutils/properties.h>
 #include <ftl/future.h>
-#include <gui/IRegionSamplingListener.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <ui/DisplayStatInfo.h>
 #include <utils/Trace.h>
@@ -150,7 +149,7 @@
     if (mSampleRequestTime.has_value()) {
         ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
         mSampleRequestTime.reset();
-        mFlinger.scheduleRegionSamplingThread();
+        mFlinger.scheduleSample();
     }
 }
 
@@ -356,10 +355,13 @@
                                                renderengine::ExternalTexture::Usage::WRITEABLE);
     }
 
-    const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-    mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
-                                 true /* regionSampling */, false /* grayscale */, captureListener);
-    ScreenCaptureResults captureResults = captureListener->waitForResults();
+    auto captureScreenResultFuture =
+            mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+                                         true /* regionSampling */, false /* grayscale */, nullptr);
+    auto& captureScreenResult = captureScreenResultFuture.get();
+    if (captureScreenResult.drawFence.ok()) {
+        sync_wait(captureScreenResult.drawFence.get(), -1);
+    }
 
     std::vector<Descriptor> activeDescriptors;
     for (const auto& descriptor : descriptors) {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 2231853..686b4b1 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android-base/thread_annotations.h>
+#include <android/gui/IRegionSamplingListener.h>
 #include <binder/IBinder.h>
 #include <renderengine/ExternalTexture.h>
 #include <ui/GraphicBuffer.h>
@@ -30,15 +31,17 @@
 #include <unordered_map>
 
 #include "Scheduler/OneShotTimer.h"
+#include "WpHash.h"
 
 namespace android {
 
-class IRegionSamplingListener;
 class Layer;
 class Scheduler;
 class SurfaceFlinger;
 struct SamplingOffsetCallback;
 
+using gui::IRegionSamplingListener;
+
 float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
                  uint32_t orientation, const Rect& area);
 
@@ -88,11 +91,6 @@
         sp<IRegionSamplingListener> listener;
     };
 
-    struct WpHash {
-        size_t operator()(const wp<IBinder>& p) const {
-            return std::hash<IBinder*>()(p.unsafe_get());
-        }
-    };
     std::vector<float> sampleBuffer(
             const sp<GraphicBuffer>& buffer, const Point& leftTop,
             const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 9a6c853..5fea521 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #include "RenderArea.h"
 
 namespace android {
@@ -33,6 +29,3 @@
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
new file mode 100644
index 0000000..409d098
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -0,0 +1,35 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_defaults {
+    name: "libscheduler_defaults",
+    defaults: ["surfaceflinger_defaults"],
+    cflags: [
+        "-DLOG_TAG=\"Scheduler\"",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+    ],
+    shared_libs: [
+        "libbase",
+        "libutils",
+    ],
+}
+
+cc_library_headers {
+    name: "libscheduler_headers",
+    defaults: ["libscheduler_defaults"],
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libscheduler",
+    defaults: ["libscheduler_defaults"],
+    srcs: [],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 2bdcaf6..627c49a 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -170,20 +170,21 @@
     mEventThread->registerDisplayEventConnection(this);
 }
 
-status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
+binder::Status EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
     outChannel->setReceiveFd(mChannel.moveReceiveFd());
     outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd())));
-    return NO_ERROR;
+    return binder::Status::ok();
 }
 
-status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
-    mEventThread->setVsyncRate(rate, this);
-    return NO_ERROR;
+binder::Status EventThreadConnection::setVsyncRate(int rate) {
+    mEventThread->setVsyncRate(static_cast<uint32_t>(rate), this);
+    return binder::Status::ok();
 }
 
-void EventThreadConnection::requestNextVsync() {
+binder::Status EventThreadConnection::requestNextVsync() {
     ATRACE_NAME("requestNextVsync");
     mEventThread->requestNextVsync(this);
+    return binder::Status::ok();
 }
 
 status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
@@ -355,14 +356,7 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     LOG_FATAL_IF(!mVSyncState);
-    const int64_t vsyncId = [&] {
-        if (mTokenManager != nullptr) {
-            return mTokenManager->generateTokenForPredictions(
-                    {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
-        }
-        return FrameTimelineInfo::INVALID_VSYNC_ID;
-    }();
-
+    const int64_t vsyncId = generateToken(timestamp, deadlineTimestamp, expectedVSyncTimestamp);
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
                                        expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
     mCondition.notify_all();
@@ -567,12 +561,48 @@
     }
 }
 
+int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+                                   nsecs_t expectedVSyncTimestamp) const {
+    if (mTokenManager != nullptr) {
+        return mTokenManager->generateTokenForPredictions(
+                {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+    }
+    return FrameTimelineInfo::INVALID_VSYNC_ID;
+}
+
+void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const {
+    // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
+    for (int multiplier = -DisplayEventReceiver::kFrameTimelinesLength + 1, currentIndex = 0;
+         currentIndex < DisplayEventReceiver::kFrameTimelinesLength; multiplier++) {
+        nsecs_t deadline = event.vsync.deadlineTimestamp + multiplier * event.vsync.frameInterval;
+        // Valid possible frame timelines must have future values.
+        if (deadline > event.header.timestamp) {
+            if (multiplier == 0) {
+                event.vsync.preferredFrameTimelineIndex = currentIndex;
+                event.vsync.frameTimelines[currentIndex] =
+                        {.vsyncId = event.vsync.vsyncId,
+                         .deadlineTimestamp = event.vsync.deadlineTimestamp,
+                         .expectedVSyncTimestamp = event.vsync.expectedVSyncTimestamp};
+            } else {
+                nsecs_t expectedVSync =
+                        event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval;
+                event.vsync.frameTimelines[currentIndex] =
+                        {.vsyncId = generateToken(event.header.timestamp, deadline, expectedVSync),
+                         .deadlineTimestamp = deadline,
+                         .expectedVSyncTimestamp = expectedVSync};
+            }
+            currentIndex++;
+        }
+    }
+}
+
 void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                 const DisplayEventConsumers& consumers) {
     for (const auto& consumer : consumers) {
         DisplayEventReceiver::Event copy = event;
         if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
             copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+            generateFrameTimeline(copy);
         }
         switch (consumer->postEvent(copy)) {
             case NO_ERROR:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 9265a25..fa9af09 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -17,8 +17,8 @@
 #pragma once
 
 #include <android-base/thread_annotations.h>
+#include <android/gui/BnDisplayEventConnection.h>
 #include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
 #include <private/gui/BitTube.h>
 #include <sys/types.h>
 #include <utils/Errors.h>
@@ -80,7 +80,7 @@
     virtual void dump(std::string& result) const = 0;
 };
 
-class EventThreadConnection : public BnDisplayEventConnection {
+class EventThreadConnection : public gui::BnDisplayEventConnection {
 public:
     EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
                           ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
@@ -88,9 +88,9 @@
 
     virtual status_t postEvent(const DisplayEventReceiver::Event& event);
 
-    status_t stealReceiveChannel(gui::BitTube* outChannel) override;
-    status_t setVsyncRate(uint32_t rate) override;
-    void requestNextVsync() override; // asynchronous
+    binder::Status stealReceiveChannel(gui::BitTube* outChannel) override;
+    binder::Status setVsyncRate(int rate) override;
+    binder::Status requestNextVsync() override; // asynchronous
 
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
@@ -204,6 +204,10 @@
     void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
                       nsecs_t deadlineTimestamp) override;
 
+    int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+                          nsecs_t expectedVSyncTimestamp) const;
+    void generateFrameTimeline(DisplayEventReceiver::Event& event) const;
+
     const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
 
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 84e3548..74a2ca7 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,42 +84,38 @@
 
 void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
     std::lock_guard lock(mLock);
-    for (const auto& info : mLayerInfos) {
-        LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str());
-    }
+    LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first !=
+                                LayerHistory::layerStatus::NotFound,
+                        "%s already registered", layer->getName().c_str());
     auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
-    mLayerInfos.emplace_back(layer, std::move(info));
+
+    // The layer can be placed on either map, it is assumed that partitionLayers() will be called
+    // to correct them.
+    mInactiveLayerInfos.insert({layer->getSequence(), std::make_pair(layer, std::move(info))});
 }
 
 void LayerHistory::deregisterLayer(Layer* layer) {
     std::lock_guard lock(mLock);
-
-    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
-                                 [layer](const auto& pair) { return pair.first == layer; });
-    LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
-
-    const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
-    if (i < mActiveLayersEnd) {
-        mActiveLayersEnd--;
+    if (!mActiveLayerInfos.erase(layer->getSequence())) {
+        if (!mInactiveLayerInfos.erase(layer->getSequence())) {
+            LOG_ALWAYS_FATAL("%s: unknown layer %p", __FUNCTION__, layer);
+        }
     }
-    const size_t last = mLayerInfos.size() - 1;
-    std::swap(mLayerInfos[i], mLayerInfos[last]);
-    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
 }
 
 void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
                           LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
+    auto id = layer->getSequence();
 
-    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
-                                 [layer](const auto& pair) { return pair.first == layer; });
-    if (it == mLayerInfos.end()) {
+    auto [found, layerPair] = findLayer(id);
+    if (found == LayerHistory::layerStatus::NotFound) {
         // Offscreen layer
         ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
         return;
     }
 
-    const auto& info = it->second;
+    const auto& info = layerPair->second;
     const auto layerProps = LayerInfo::LayerProps{
             .visible = layer->isVisible(),
             .bounds = layer->getBounds(),
@@ -131,9 +127,10 @@
     info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
 
     // Activate layer if inactive.
-    if (const auto end = activeLayers().end(); it >= end) {
-        std::iter_swap(it, end);
-        mActiveLayersEnd++;
+    if (found == LayerHistory::layerStatus::LayerInInactiveMap) {
+        mActiveLayerInfos.insert(
+                {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+        mInactiveLayerInfos.erase(id);
     }
 }
 
@@ -145,7 +142,8 @@
 
     partitionLayers(now);
 
-    for (const auto& [layer, info] : activeLayers()) {
+    for (const auto& [key, value] : mActiveLayerInfos) {
+        auto& info = value.second;
         const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
         const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
         ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
@@ -179,12 +177,29 @@
 void LayerHistory::partitionLayers(nsecs_t now) {
     const nsecs_t threshold = getActiveLayerThreshold(now);
 
-    // Collect expired and inactive layers after active layers.
-    size_t i = 0;
-    while (i < mActiveLayersEnd) {
-        auto& [layerUnsafe, info] = mLayerInfos[i];
+    // iterate over inactive map
+    LayerInfos::iterator it = mInactiveLayerInfos.begin();
+    while (it != mInactiveLayerInfos.end()) {
+        auto& [layerUnsafe, info] = it->second;
         if (isLayerActive(*info, threshold)) {
-            i++;
+            // move this to the active map
+
+            mActiveLayerInfos.insert({it->first, std::move(it->second)});
+            it = mInactiveLayerInfos.erase(it);
+        } else {
+            if (CC_UNLIKELY(mTraceEnabled)) {
+                trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+            }
+            info->onLayerInactive(now);
+            it++;
+        }
+    }
+
+    // iterate over active map
+    it = mActiveLayerInfos.begin();
+    while (it != mActiveLayerInfos.end()) {
+        auto& [layerUnsafe, info] = it->second;
+        if (isLayerActive(*info, threshold)) {
             // Set layer vote if set
             const auto frameRate = info->getSetFrameRateVote();
             const auto voteType = [&]() {
@@ -206,30 +221,68 @@
             } else {
                 info->resetLayerVote();
             }
-            continue;
-        }
 
-        if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+            it++;
+        } else {
+            if (CC_UNLIKELY(mTraceEnabled)) {
+                trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+            }
+            info->onLayerInactive(now);
+            // move this to the inactive map
+            mInactiveLayerInfos.insert({it->first, std::move(it->second)});
+            it = mActiveLayerInfos.erase(it);
         }
-
-        info->onLayerInactive(now);
-        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
     }
 }
 
 void LayerHistory::clear() {
     std::lock_guard lock(mLock);
-
-    for (const auto& [layer, info] : activeLayers()) {
-        info->clearHistory(systemTime());
+    for (const auto& [key, value] : mActiveLayerInfos) {
+        value.second->clearHistory(systemTime());
     }
 }
 
 std::string LayerHistory::dump() const {
     std::lock_guard lock(mLock);
-    return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
-                              mActiveLayersEnd);
+    return base::StringPrintf("LayerHistory{size=%zu, active=%zu}",
+                              mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
+                              mActiveLayerInfos.size());
+}
+
+float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const {
+    std::lock_guard lock(mLock);
+    auto [found, layerPair] = findLayer(id);
+    if (found != LayerHistory::layerStatus::NotFound) {
+        return layerPair->second->getFps(now).getValue();
+    }
+    return 0.f;
+}
+
+std::pair<LayerHistory::layerStatus, LayerHistory::LayerPair*> LayerHistory::findLayer(int32_t id) {
+    // the layer could be in either the active or inactive map, try both
+    auto it = mActiveLayerInfos.find(id);
+    if (it != mActiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+    }
+    it = mInactiveLayerInfos.find(id);
+    if (it != mInactiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+    }
+    return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
+}
+
+std::pair<LayerHistory::layerStatus, const LayerHistory::LayerPair*> LayerHistory::findLayer(
+        int32_t id) const {
+    // the layer could be in either the active or inactive map, try both
+    auto it = mActiveLayerInfos.find(id);
+    if (it != mActiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+    }
+    it = mInactiveLayerInfos.find(id);
+    if (it != mInactiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+    }
+    return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 92236f5..cc55700 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -20,6 +20,7 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
+#include <map>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -31,11 +32,9 @@
 namespace android {
 
 class Layer;
-class TestableScheduler;
 
 namespace scheduler {
 
-class LayerHistoryTest;
 class LayerInfo;
 
 class LayerHistory {
@@ -74,34 +73,43 @@
     void deregisterLayer(Layer*);
     std::string dump() const;
 
+    // return the frames per second of the layer with the given sequence id.
+    float getLayerFramerate(nsecs_t now, int32_t id) const;
+
 private:
-    friend LayerHistoryTest;
-    friend TestableScheduler;
+    friend class LayerHistoryTest;
+    friend class TestableScheduler;
 
     using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
-    using LayerInfos = std::vector<LayerPair>;
+    // keyed by id as returned from Layer::getSequence()
+    using LayerInfos = std::unordered_map<int32_t, LayerPair>;
 
-    struct ActiveLayers {
-        LayerInfos& infos;
-        const size_t index;
+    // Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive
+    // layers to mInactiveLayerInfos.
+    // worst case time complexity is O(2 * inactive + active)
+    void partitionLayers(nsecs_t now) REQUIRES(mLock);
 
-        auto begin() { return infos.begin(); }
-        auto end() { return begin() + static_cast<long>(index); }
+    enum class layerStatus {
+        NotFound,
+        LayerInActiveMap,
+        LayerInInactiveMap,
     };
 
-    ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
-
-    // Iterates over layers in a single pass, swapping pairs such that active layers precede
-    // inactive layers, and inactive layers precede expired layers. Removes expired layers by
-    // truncating after inactive layers.
-    void partitionLayers(nsecs_t now) REQUIRES(mLock);
+    // looks up a layer by sequence id in both layerInfo maps.
+    // The first element indicates if and where the item was found
+    std::pair<layerStatus, LayerHistory::LayerPair*> findLayer(int32_t id) REQUIRES(mLock);
+    std::pair<layerStatus, const LayerHistory::LayerPair*> findLayer(int32_t id) const
+            REQUIRES(mLock);
 
     mutable std::mutex mLock;
 
-    // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
-    // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
-    LayerInfos mLayerInfos GUARDED_BY(mLock);
-    size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+    // Partitioned into two maps to facility two kinds of retrieval:
+    // 1. retrieval of a layer by id (attempt lookup in both maps)
+    // 2. retrieval of all active layers (iterate that map)
+    // The partitioning is allowed to become out of date but calling partitionLayers refreshes the
+    // validity of each map.
+    LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
+    LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
 
     uint32_t mDisplayArea = 0;
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 8a45b66..943615c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -28,6 +28,7 @@
 
 #include <cutils/compiler.h>
 #include <cutils/trace.h>
+#include <ftl/enum.h>
 
 #undef LOG_TAG
 #define LOG_TAG "LayerInfo"
@@ -41,7 +42,7 @@
       : mName(name),
         mOwnerUid(ownerUid),
         mDefaultVote(defaultVote),
-        mLayerVote({defaultVote, Fps(0.0f)}),
+        mLayerVote({defaultVote, Fps()}),
         mRefreshRateHistory(name) {}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
@@ -74,12 +75,16 @@
 }
 
 bool LayerInfo::isFrequent(nsecs_t now) const {
+    using fps_approx_ops::operator>=;
     // If we know nothing about this layer we consider it as frequent as it might be the start
     // of an animation.
     if (mFrameTimes.size() < kFrequentLayerWindowSize) {
         return true;
     }
+    return getFps(now) >= kMinFpsForFrequentLayer;
+}
 
+Fps LayerInfo::getFps(nsecs_t now) const {
     // Find the first active frame
     auto it = mFrameTimes.begin();
     for (; it != mFrameTimes.end(); ++it) {
@@ -90,13 +95,12 @@
 
     const auto numFrames = std::distance(it, mFrameTimes.end());
     if (numFrames < kFrequentLayerWindowSize) {
-        return false;
+        return Fps();
     }
 
     // Layer is considered frequent if the average frame rate is higher than the threshold
     const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
-    return Fps::fromPeriodNsecs(totalTime / (numFrames - 1))
-            .greaterThanOrEqualWithMargin(kMinFpsForFrequentLayer);
+    return Fps::fromPeriodNsecs(totalTime / (numFrames - 1));
 }
 
 bool LayerInfo::isAnimating(nsecs_t now) const {
@@ -191,17 +195,17 @@
         return std::nullopt;
     }
 
-    const auto averageFrameTime = calculateAverageFrameTime();
-    if (averageFrameTime.has_value()) {
+    if (const auto averageFrameTime = calculateAverageFrameTime()) {
         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
         const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
         if (refreshRateConsistent) {
             const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
-            // To avoid oscillation, use the last calculated refresh rate if it is
-            // close enough
+            using fps_approx_ops::operator!=;
+
+            // To avoid oscillation, use the last calculated refresh rate if it is close enough.
             if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
                         MARGIN &&
-                !mLastRefreshRate.reported.equalsWithMargin(knownRefreshRate)) {
+                mLastRefreshRate.reported != knownRefreshRate) {
                 mLastRefreshRate.calculated = refreshRate;
                 mLastRefreshRate.reported = knownRefreshRate;
             }
@@ -228,7 +232,7 @@
     if (isAnimating(now)) {
         ALOGV("%s is animating", mName.c_str());
         mLastRefreshRate.animatingOrInfrequent = true;
-        return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+        return {LayerHistory::LayerVoteType::Max, Fps()};
     }
 
     if (!isFrequent(now)) {
@@ -236,7 +240,7 @@
         mLastRefreshRate.animatingOrInfrequent = true;
         // Infrequent layers vote for mininal refresh rate for
         // battery saving purposes and also to prevent b/135718869.
-        return {LayerHistory::LayerVoteType::Min, Fps(0.0f)};
+        return {LayerHistory::LayerVoteType::Min, Fps()};
     }
 
     // If the layer was previously tagged as animating or infrequent, we clear
@@ -253,13 +257,13 @@
     }
 
     ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
-    return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+    return {LayerHistory::LayerVoteType::Max, Fps()};
 }
 
-const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
     if (mTraceTags.count(type) == 0) {
-        const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
-        mTraceTags.emplace(type, tag);
+        auto tag = "LFPS " + mName + " " + ftl::enum_string(type);
+        mTraceTags.emplace(type, std::move(tag));
     }
 
     return mTraceTags.at(type).c_str();
@@ -300,9 +304,13 @@
 bool LayerInfo::RefreshRateHistory::isConsistent() const {
     if (mRefreshRates.empty()) return true;
 
-    const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
-    const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
-    const auto consistent =
+    const auto [min, max] =
+            std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
+                                [](const auto& lhs, const auto& rhs) {
+                                    return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
+                                });
+
+    const bool consistent =
             max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
 
     if (CC_UNLIKELY(sTraceEnabled)) {
@@ -321,4 +329,4 @@
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index ce9783c..2d88a4f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,15 +16,19 @@
 
 #pragma once
 
+#include <chrono>
+#include <deque>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
 #include <ui/Transform.h>
 #include <utils/Timers.h>
 
-#include <chrono>
-#include <deque>
+#include <scheduler/Seamlessness.h>
 
 #include "LayerHistory.h"
 #include "RefreshRateConfigs.h"
-#include "Scheduler/Seamlessness.h"
 #include "SchedulerUtils.h"
 
 namespace android {
@@ -51,7 +55,7 @@
     // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
     // favor of a low refresh rate.
     static constexpr size_t kFrequentLayerWindowSize = 3;
-    static constexpr Fps kMinFpsForFrequentLayer{10.0f};
+    static constexpr Fps kMinFpsForFrequentLayer = 10_Hz;
     static constexpr auto kMaxPeriodForFrequentLayerNs =
             std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms;
 
@@ -62,7 +66,7 @@
     // Holds information about the layer vote
     struct LayerVote {
         LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
-        Fps fps{0.0f};
+        Fps fps;
         Seamlessness seamlessness = Seamlessness::Default;
     };
 
@@ -78,6 +82,8 @@
 
         NoVote, // Layer doesn't have any requirements for the refresh rate and
                 // should not be considered when the display refresh rate is determined.
+
+        ftl_last = NoVote
     };
 
     // Encapsulates the frame rate and compatibility of the layer. This information will be used
@@ -86,19 +92,17 @@
         using Seamlessness = scheduler::Seamlessness;
 
         Fps rate;
-        FrameRateCompatibility type;
-        Seamlessness seamlessness;
+        FrameRateCompatibility type = FrameRateCompatibility::Default;
+        Seamlessness seamlessness = Seamlessness::Default;
 
-        FrameRate()
-              : rate(0),
-                type(FrameRateCompatibility::Default),
-                seamlessness(Seamlessness::Default) {}
+        FrameRate() = default;
+
         FrameRate(Fps rate, FrameRateCompatibility type,
                   Seamlessness seamlessness = Seamlessness::OnlySeamless)
               : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
 
         bool operator==(const FrameRate& other) const {
-            return rate.equalsWithMargin(other.rate) && type == other.type &&
+            return isApproxEqual(rate, other.rate) && type == other.type &&
                     seamlessness == other.seamlessness;
         }
 
@@ -151,7 +155,7 @@
     void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
 
     // Resets the layer vote to its default.
-    void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
+    void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default}; }
 
     std::string getName() const { return mName; }
 
@@ -174,6 +178,9 @@
     // Returns a C string for tracing a vote
     const char* getTraceTag(LayerHistory::LayerVoteType type) const;
 
+    // Return the framerate of this layer.
+    Fps getFps(nsecs_t now) const;
+
     void onLayerInactive(nsecs_t now) {
         // Mark mFrameTimeValidSince to now to ignore all previous frame times.
         // We are not deleting the old frame to keep track of whether we should treat the first
@@ -201,9 +208,9 @@
     // Holds information about the calculated and reported refresh rate
     struct RefreshRateHeuristicData {
         // Rate calculated on the layer
-        Fps calculated{0.0f};
+        Fps calculated;
         // Last reported rate for LayerInfo::getRefreshRate()
-        Fps reported{0.0f};
+        Fps reported;
         // Whether the last reported rate for LayerInfo::getRefreshRate()
         // was due to animation or infrequent updates
         bool animatingOrInfrequent = false;
@@ -229,14 +236,8 @@
 
         // Holds the refresh rate when it was calculated
         struct RefreshRateData {
-            Fps refreshRate{0.0f};
+            Fps refreshRate;
             nsecs_t timestamp = 0;
-
-            bool operator<(const RefreshRateData& other) const {
-                // We don't need comparison with margins since we are using
-                // this to find the min and max refresh rates.
-                return refreshRate.getValue() < other.refreshRate.getValue();
-            }
         };
 
         // Holds tracing strings
@@ -268,7 +269,7 @@
 
     // Used for sanitizing the heuristic data. If two frames are less than
     // this period apart from each other they'll be considered as duplicates.
-    static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs();
+    static constexpr nsecs_t kMinPeriodBetweenFrames = (240_Hz).getPeriodNsecs();
     // Used for sanitizing the heuristic data. If two frames are more than
     // this period apart from each other, the interval between them won't be
     // taken into account when calculating average frame rate.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 4d51125..a020e2c 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -27,49 +27,44 @@
 #include "EventThread.h"
 #include "FrameTimeline.h"
 #include "MessageQueue.h"
-#include "SurfaceFlinger.h"
 
 namespace android::impl {
 
-void MessageQueue::Handler::dispatchRefresh() {
-    if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) {
-        mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
-    }
-}
-
-void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
-    if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) {
+void MessageQueue::Handler::dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime) {
+    if (!mFramePending.exchange(true)) {
         mVsyncId = vsyncId;
-        mExpectedVSyncTime = expectedVSyncTimestamp;
-        mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
+        mExpectedVsyncTime = expectedVsyncTime;
+        mQueue.mLooper->sendMessage(this, Message());
     }
 }
 
-bool MessageQueue::Handler::invalidatePending() {
-    constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh;
-    return (mEventMask.load() & pendingMask) != 0;
+bool MessageQueue::Handler::isFramePending() const {
+    return mFramePending.load();
 }
 
-void MessageQueue::Handler::handleMessage(const Message& message) {
-    switch (message.what) {
-        case INVALIDATE:
-            mEventMask.fetch_and(~eventMaskInvalidate);
-            mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
-            break;
-        case REFRESH:
-            mEventMask.fetch_and(~eventMaskRefresh);
-            mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
-            break;
+void MessageQueue::Handler::handleMessage(const Message&) {
+    mFramePending.store(false);
+
+    const nsecs_t frameTime = systemTime();
+    auto& compositor = mQueue.mCompositor;
+
+    if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) {
+        return;
     }
+
+    compositor.composite(frameTime);
+    compositor.sample();
 }
 
-// ---------------------------------------------------------------------------
+MessageQueue::MessageQueue(ICompositor& compositor)
+      : MessageQueue(compositor, sp<Handler>::make(*this)) {}
 
-void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
-    mFlinger = flinger;
-    mLooper = new Looper(true);
-    mHandler = new Handler(*this);
-}
+constexpr bool kAllowNonCallbacks = true;
+
+MessageQueue::MessageQueue(ICompositor& compositor, sp<Handler> handler)
+      : mCompositor(compositor),
+        mLooper(sp<Looper>::make(kAllowNonCallbacks)),
+        mHandler(std::move(handler)) {}
 
 // TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
 // and remove the EventThread from MessageQueue
@@ -110,11 +105,13 @@
     {
         std::lock_guard lock(mVsync.mutex);
         mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
-        mVsync.scheduled = false;
+        mVsync.scheduledFrameTime.reset();
     }
-    mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
-                                         {targetWakeupTime, readyTime, vsyncTime}),
-                                 vsyncTime);
+
+    const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions(
+            {targetWakeupTime, readyTime, vsyncTime});
+
+    mHandler->dispatchFrame(vsyncId, vsyncTime);
 }
 
 void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
@@ -135,8 +132,8 @@
     ATRACE_CALL();
     std::lock_guard lock(mVsync.mutex);
     mVsync.workDuration = workDuration;
-    if (mVsync.scheduled) {
-        mVsync.expectedWakeupTime = mVsync.registration->schedule(
+    if (mVsync.scheduledFrameTime) {
+        mVsync.scheduledFrameTime = mVsync.registration->schedule(
                 {mVsync.workDuration.get().count(),
                  /*readyDuration=*/0, mVsync.lastCallbackTime.count()});
     }
@@ -168,7 +165,7 @@
     mLooper->sendMessage(handler, Message());
 }
 
-void MessageQueue::invalidate() {
+void MessageQueue::scheduleFrame() {
     ATRACE_CALL();
 
     {
@@ -181,41 +178,34 @@
     }
 
     std::lock_guard lock(mVsync.mutex);
-    mVsync.scheduled = true;
-    mVsync.expectedWakeupTime =
+    mVsync.scheduledFrameTime =
             mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
                                            .readyDuration = 0,
                                            .earliestVsync = mVsync.lastCallbackTime.count()});
 }
 
-void MessageQueue::refresh() {
-    mHandler->dispatchRefresh();
-}
-
 void MessageQueue::injectorCallback() {
     ssize_t n;
     DisplayEventReceiver::Event buffer[8];
     while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
         for (int i = 0; i < n; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
-                                             buffer[i].vsync.expectedVSyncTimestamp);
+                auto& vsync = buffer[i].vsync;
+                mHandler->dispatchFrame(vsync.vsyncId, vsync.expectedVSyncTimestamp);
                 break;
             }
         }
     }
 }
 
-std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() {
-    if (mHandler->invalidatePending()) {
-        return std::chrono::steady_clock::now();
+auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
+    if (mHandler->isFramePending()) {
+        return Clock::now();
     }
 
     std::lock_guard lock(mVsync.mutex);
-    if (mVsync.scheduled) {
-        LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled");
-        const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime);
-        return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime);
+    if (const auto time = mVsync.scheduledFrameTime) {
+        return Clock::time_point(std::chrono::nanoseconds(*time));
     }
 
     return std::nullopt;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 58ce9b9..9532e26 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -22,7 +22,7 @@
 #include <utility>
 
 #include <android-base/thread_annotations.h>
-#include <gui/IDisplayEventConnection.h>
+#include <android/gui/IDisplayEventConnection.h>
 #include <private/gui/BitTube.h>
 #include <utils/Looper.h>
 #include <utils/Timers.h>
@@ -33,7 +33,14 @@
 
 namespace android {
 
-class SurfaceFlinger;
+struct ICompositor {
+    virtual bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) = 0;
+    virtual void composite(nsecs_t frameTime) = 0;
+    virtual void sample() = 0;
+
+protected:
+    ~ICompositor() = default;
+};
 
 template <typename F>
 class Task : public MessageHandler {
@@ -56,65 +63,60 @@
 
 class MessageQueue {
 public:
-    enum {
-        INVALIDATE = 0,
-        REFRESH = 1,
-    };
-
     virtual ~MessageQueue() = default;
 
-    virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
     virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                            std::chrono::nanoseconds workDuration) = 0;
     virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
     virtual void setInjector(sp<EventThreadConnection>) = 0;
     virtual void waitMessage() = 0;
     virtual void postMessage(sp<MessageHandler>&&) = 0;
-    virtual void invalidate() = 0;
-    virtual void refresh() = 0;
-    virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0;
-};
+    virtual void scheduleFrame() = 0;
 
-// ---------------------------------------------------------------------------
+    using Clock = std::chrono::steady_clock;
+    virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0;
+};
 
 namespace impl {
 
 class MessageQueue : public android::MessageQueue {
 protected:
     class Handler : public MessageHandler {
-        enum : uint32_t {
-            eventMaskInvalidate = 0x1,
-            eventMaskRefresh = 0x2,
-            eventMaskTransaction = 0x4
-        };
         MessageQueue& mQueue;
-        std::atomic<uint32_t> mEventMask;
-        std::atomic<int64_t> mVsyncId;
-        std::atomic<nsecs_t> mExpectedVSyncTime;
+        std::atomic_bool mFramePending = false;
+        std::atomic<int64_t> mVsyncId = 0;
+        std::atomic<nsecs_t> mExpectedVsyncTime = 0;
 
     public:
-        explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
+        explicit Handler(MessageQueue& queue) : mQueue(queue) {}
         void handleMessage(const Message& message) override;
-        virtual void dispatchRefresh();
-        virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
-        virtual bool invalidatePending();
+
+        bool isFramePending() const;
+
+        virtual void dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime);
     };
 
     friend class Handler;
 
-    sp<SurfaceFlinger> mFlinger;
-    sp<Looper> mLooper;
+    // For tests.
+    MessageQueue(ICompositor&, sp<Handler>);
+
+    void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
+
+private:
+    ICompositor& mCompositor;
+    const sp<Looper> mLooper;
+    const sp<Handler> mHandler;
 
     struct Vsync {
         frametimeline::TokenManager* tokenManager = nullptr;
         std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
 
-        std::mutex mutex;
+        mutable std::mutex mutex;
         TracedOrdinal<std::chrono::nanoseconds> workDuration
                 GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
         std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
-        bool scheduled GUARDED_BY(mutex) = false;
-        std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex);
+        std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex);
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
@@ -127,14 +129,11 @@
     Vsync mVsync;
     Injector mInjector;
 
-    sp<Handler> mHandler;
-
-    void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
     void injectorCallback();
 
 public:
-    ~MessageQueue() override = default;
-    void init(const sp<SurfaceFlinger>& flinger) override;
+    explicit MessageQueue(ICompositor&);
+
     void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                    std::chrono::nanoseconds workDuration) override;
     void setDuration(std::chrono::nanoseconds workDuration) override;
@@ -143,13 +142,9 @@
     void waitMessage() override;
     void postMessage(sp<MessageHandler>&&) override;
 
-    // sends INVALIDATE message at next VSYNC
-    void invalidate() override;
+    void scheduleFrame() override;
 
-    // sends REFRESH message at next VSYNC
-    void refresh() override;
-
-    std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override;
+    std::optional<Clock::time_point> getScheduledFrameTime() const override;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0ee9fb3..71d5631 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -21,40 +21,43 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wextra"
 
-#include "RefreshRateConfigs.h"
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <utils/Trace.h>
 #include <chrono>
 #include <cmath>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <utils/Trace.h>
+
 #include "../SurfaceFlingerProperties.h"
+#include "RefreshRateConfigs.h"
 
 #undef LOG_TAG
 #define LOG_TAG "RefreshRateConfigs"
 
 namespace android::scheduler {
 namespace {
+
 std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
     return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(),
-                              RefreshRateConfigs::layerVoteTypeString(layer.vote).c_str(), weight,
-                              toString(layer.seamlessness).c_str(),
+                              ftl::enum_string(layer.vote).c_str(), weight,
+                              ftl::enum_string(layer.seamlessness).c_str(),
                               to_string(layer.desiredRefreshRate).c_str());
 }
 
 std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
-    std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
+    std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
     knownFrameRates.reserve(knownFrameRates.size() + modes.size());
 
-    // Add all supported refresh rates to the set
+    // Add all supported refresh rates.
     for (const auto& mode : modes) {
-        const auto refreshRate = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
-        knownFrameRates.emplace_back(refreshRate);
+        knownFrameRates.push_back(Fps::fromPeriodNsecs(mode->getVsyncPeriod()));
     }
 
-    // Sort and remove duplicates
-    std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
+    // Sort and remove duplicates.
+    std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
     knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
-                                      Fps::EqualsWithMargin()),
+                                      isApproxEqual),
                           knownFrameRates.end());
     return knownFrameRates;
 }
@@ -64,31 +67,17 @@
 using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
 using RefreshRate = RefreshRateConfigs::RefreshRate;
 
+bool RefreshRate::inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
+    using fps_approx_ops::operator<=;
+    return minRefreshRate <= getFps() && getFps() <= maxRefreshRate;
+}
+
 std::string RefreshRate::toString() const {
     return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
                               getModeId().value(), mode->getHwcId(), getFps().getValue(),
                               mode->getWidth(), mode->getHeight(), getModeGroup());
 }
 
-std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
-    switch (vote) {
-        case LayerVoteType::NoVote:
-            return "NoVote";
-        case LayerVoteType::Min:
-            return "Min";
-        case LayerVoteType::Max:
-            return "Max";
-        case LayerVoteType::Heuristic:
-            return "Heuristic";
-        case LayerVoteType::ExplicitDefault:
-            return "ExplicitDefault";
-        case LayerVoteType::ExplicitExactOrMultiple:
-            return "ExplicitExactOrMultiple";
-        case LayerVoteType::ExplicitExact:
-            return "ExplicitExact";
-    }
-}
-
 std::string RefreshRateConfigs::Policy::toString() const {
     return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d"
                               ", primary range: %s, app request range: %s",
@@ -110,14 +99,14 @@
 
 bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
                                        const RefreshRate& refreshRate) const {
+    using namespace fps_approx_ops;
+
     switch (layer.vote) {
         case LayerVoteType::ExplicitExactOrMultiple:
         case LayerVoteType::Heuristic:
             if (mConfig.frameRateMultipleThreshold != 0 &&
-                refreshRate.getFps().greaterThanOrEqualWithMargin(
-                        Fps(mConfig.frameRateMultipleThreshold)) &&
-                layer.desiredRefreshRate.lessThanWithMargin(
-                        Fps(mConfig.frameRateMultipleThreshold / 2))) {
+                refreshRate.getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
+                layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
                 // Don't vote high refresh rates past the threshold for layers with a low desired
                 // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
                 // 120 Hz, but desired 60 fps should have a vote.
@@ -134,6 +123,68 @@
     return true;
 }
 
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
+        const LayerRequirement& layer, const RefreshRate& refreshRate) const {
+    constexpr float kScoreForFractionalPairs = .8f;
+
+    const auto displayPeriod = refreshRate.getVsyncPeriod();
+    const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+    if (layer.vote == LayerVoteType::ExplicitDefault) {
+        // Find the actual rate the layer will render, assuming
+        // that layerPeriod is the minimal period to render a frame.
+        // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+        // then the actualLayerPeriod will be 32ms, because it is the
+        // smallest multiple of the display period which is >= layerPeriod.
+        auto actualLayerPeriod = displayPeriod;
+        int multiplier = 1;
+        while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+            multiplier++;
+            actualLayerPeriod = displayPeriod * multiplier;
+        }
+
+        // Because of the threshold we used above it's possible that score is slightly
+        // above 1.
+        return std::min(1.0f,
+                        static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+    }
+
+    if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+        layer.vote == LayerVoteType::Heuristic) {
+        if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
+            return kScoreForFractionalPairs;
+        }
+
+        // Calculate how many display vsyncs we need to present a single frame for this
+        // layer
+        const auto [displayFramesQuotient, displayFramesRemainder] =
+                getDisplayFrames(layerPeriod, displayPeriod);
+        static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+        if (displayFramesRemainder == 0) {
+            // Layer desired refresh rate matches the display rate.
+            return 1.0f;
+        }
+
+        if (displayFramesQuotient == 0) {
+            // Layer desired refresh rate is higher than the display rate.
+            return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+                    (1.0f / (MAX_FRAMES_TO_FIT + 1));
+        }
+
+        // Layer desired refresh rate is lower than the display rate. Check how well it fits
+        // the cadence.
+        auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+        int iter = 2;
+        while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+            diff = diff - (displayPeriod - diff);
+            iter++;
+        }
+
+        return (1.0f / iter);
+    }
+
+    return 0;
+}
+
 float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
                                                     const RefreshRate& refreshRate,
                                                     bool isSeamlessSwitch) const {
@@ -153,51 +204,6 @@
         return ratio * ratio;
     }
 
-    const auto displayPeriod = refreshRate.getVsyncPeriod();
-    const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
-    if (layer.vote == LayerVoteType::ExplicitDefault) {
-        // Find the actual rate the layer will render, assuming
-        // that layerPeriod is the minimal time to render a frame
-        auto actualLayerPeriod = displayPeriod;
-        int multiplier = 1;
-        while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
-            multiplier++;
-            actualLayerPeriod = displayPeriod * multiplier;
-        }
-        return std::min(1.0f,
-                        static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
-    }
-
-    if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
-        layer.vote == LayerVoteType::Heuristic) {
-        // Calculate how many display vsyncs we need to present a single frame for this
-        // layer
-        const auto [displayFramesQuotient, displayFramesRemainder] =
-                getDisplayFrames(layerPeriod, displayPeriod);
-        static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
-        if (displayFramesRemainder == 0) {
-            // Layer desired refresh rate matches the display rate.
-            return 1.0f * seamlessness;
-        }
-
-        if (displayFramesQuotient == 0) {
-            // Layer desired refresh rate is higher than the display rate.
-            return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
-                    (1.0f / (MAX_FRAMES_TO_FIT + 1));
-        }
-
-        // Layer desired refresh rate is lower than the display rate. Check how well it fits
-        // the cadence.
-        auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
-        int iter = 2;
-        while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
-            diff = diff - (displayPeriod - diff);
-            iter++;
-        }
-
-        return (1.0f / iter) * seamlessness;
-    }
-
     if (layer.vote == LayerVoteType::ExplicitExact) {
         const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate);
         if (mSupportsFrameRateOverride) {
@@ -210,7 +216,18 @@
         return divider == 1;
     }
 
-    return 0;
+    // If the layer frame rate is a divider of the refresh rate it should score
+    // the highest score.
+    if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+        return 1.0f * seamlessness;
+    }
+
+    // The layer frame rate is not a divider of the refresh rate,
+    // there is a small penalty attached to the score to favor the frame rates
+    // the exactly matches the display refresh rate or a multiple.
+    constexpr float kNonExactMatchingPenalty = 0.99f;
+    return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+            kNonExactMatchingPenalty;
 }
 
 struct RefreshRateScore {
@@ -219,7 +236,7 @@
 };
 
 RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                                   const GlobalSignals& globalSignals,
+                                                   GlobalSignals globalSignals,
                                                    GlobalSignals* outSignalsConsidered) const {
     std::lock_guard lock(mLock);
 
@@ -241,7 +258,7 @@
 }
 
 std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
-        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
         GlobalSignals* outSignalsConsidered) const {
     const bool sameAsLastCall = lastBestRefreshRateInvocation &&
             lastBestRefreshRateInvocation->layerRequirements == layers &&
@@ -258,7 +275,7 @@
 }
 
 RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
-        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
         GlobalSignals* outSignalsConsidered) const {
     ATRACE_CALL();
     ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -319,20 +336,30 @@
     const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
             explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
 
+    const Policy* policy = getCurrentPolicyLocked();
+    const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
+    // If the default mode group is different from the group of current mode,
+    // this means a layer requesting a seamed mode switch just disappeared and
+    // we should switch back to the default group.
+    // However if a seamed layer is still present we anchor around the group
+    // of the current mode, in order to prevent unnecessary seamed mode switches
+    // (e.g. when pausing a video playback).
+    const auto anchorGroup = seamedFocusedLayers > 0 ? mCurrentRefreshRate->getModeGroup()
+                                                     : defaultMode->getModeGroup();
+
     // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
     // selected a refresh rate to see if we should apply touch boost.
     if (globalSignals.touch && !hasExplicitVoteLayers) {
         ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
         setTouchConsidered();
-        return getMaxRefreshRateByPolicyLocked();
+        return getMaxRefreshRateByPolicyLocked(anchorGroup);
     }
 
     // If the primary range consists of a single refresh rate then we can only
     // move out the of range if layers explicitly request a different refresh
     // rate.
-    const Policy* policy = getCurrentPolicyLocked();
     const bool primaryRangeIsSingleRate =
-            policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max);
+            isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
 
     if (!globalSignals.touch && globalSignals.idle &&
         !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
@@ -342,7 +369,9 @@
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
-        return getMaxRefreshRateByPolicyLocked();
+        const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+        ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str());
+        return refreshRate;
     }
 
     // Only if all layers want Min we should return Min
@@ -359,11 +388,9 @@
         scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
     }
 
-    const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
-
     for (const auto& layer : layers) {
         ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
-              layerVoteTypeString(layer.vote).c_str(), layer.weight,
+              ftl::enum_string(layer.vote).c_str(), layer.weight,
               layer.desiredRefreshRate.getValue());
         if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
             continue;
@@ -398,10 +425,7 @@
             // mode group otherwise. In second case, if the current mode group is different
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
-            const bool isInPolicyForDefault = seamedFocusedLayers > 0
-                    ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
-                    : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
-
+            const bool isInPolicyForDefault = scores[i].refreshRate->getModeGroup() == anchorGroup;
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
                       scores[i].refreshRate->toString().c_str(),
@@ -422,7 +446,7 @@
 
             const auto layerScore =
                     calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
-            ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+            ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
                   scores[i].refreshRate->getName().c_str(), layerScore);
             scores[i].score += weight * layerScore;
         }
@@ -440,9 +464,9 @@
         // range instead of picking a random score from the app range.
         if (std::all_of(scores.begin(), scores.end(),
                         [](RefreshRateScore score) { return score.score == 0; })) {
-            ALOGV("layers not scored - choose %s",
-                  getMaxRefreshRateByPolicyLocked().getName().c_str());
-            return getMaxRefreshRateByPolicyLocked();
+            const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+            ALOGV("layers not scored - choose %s", refreshRate.getName().c_str());
+            return refreshRate;
         } else {
             return *bestRefreshRate;
         }
@@ -452,7 +476,7 @@
     // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
     // vote we should not change it if we get a touch event. Only apply touch boost if it will
     // actually increase the refresh rate over the normal selection.
-    const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
+    const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
 
     const bool touchBoostForExplicitExact = [&] {
         if (mSupportsFrameRateOverride) {
@@ -463,8 +487,11 @@
             return explicitExact == 0;
         }
     }();
+
+    using fps_approx_ops::operator<;
+
     if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        bestRefreshRate->getFps().lessThanWithMargin(touchRefreshRate.getFps())) {
+        bestRefreshRate->getFps() < touchRefreshRate.getFps()) {
         setTouchConsidered();
         ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
         return touchRefreshRate;
@@ -517,7 +544,8 @@
 }
 
 RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
-        const std::vector<LayerRequirement>& layers, Fps displayFrameRate, bool touch) const {
+        const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
+        GlobalSignals globalSignals) const {
     ATRACE_CALL();
     if (!mSupportsFrameRateOverride) return {};
 
@@ -535,7 +563,7 @@
                                 return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
                             });
 
-        if (touch && hasExplicitExactOrMultiple) {
+        if (globalSignals.touch && hasExplicitExactOrMultiple) {
             continue;
         }
 
@@ -583,7 +611,7 @@
 
 template <typename Iter>
 const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
-    constexpr auto EPSILON = 0.001f;
+    constexpr auto kEpsilon = 0.0001f;
     const RefreshRate* bestRefreshRate = begin->refreshRate;
     float max = begin->score;
     for (auto i = begin; i != end; ++i) {
@@ -592,7 +620,7 @@
 
         ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100));
 
-        if (score > max * (1 + EPSILON)) {
+        if (score > max * (1 + kEpsilon)) {
             max = score;
             bestRefreshRate = refreshRate;
         }
@@ -635,10 +663,10 @@
     return getMaxRefreshRateByPolicyLocked();
 }
 
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
     for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
         const auto& refreshRate = (**it);
-        if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) {
+        if (anchorGroup == refreshRate.getModeGroup()) {
             return refreshRate;
         }
     }
@@ -762,8 +790,10 @@
         ALOGE("Default mode is not in the primary range.");
         return false;
     }
-    return policy.appRequestRange.min.lessThanOrEqualWithMargin(policy.primaryRange.min) &&
-            policy.appRequestRange.max.greaterThanOrEqualWithMargin(policy.primaryRange.max);
+
+    using namespace fps_approx_ops;
+    return policy.appRequestRange.min <= policy.primaryRange.min &&
+            policy.appRequestRange.max >= policy.primaryRange.max;
 }
 
 status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
@@ -889,19 +919,21 @@
 }
 
 Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
-    if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
-        return *mKnownFrameRates.begin();
+    using namespace fps_approx_ops;
+
+    if (frameRate <= mKnownFrameRates.front()) {
+        return mKnownFrameRates.front();
     }
 
-    if (frameRate.greaterThanOrEqualWithMargin(*std::prev(mKnownFrameRates.end()))) {
-        return *std::prev(mKnownFrameRates.end());
+    if (frameRate >= mKnownFrameRates.back()) {
+        return mKnownFrameRates.back();
     }
 
     auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
-                                       Fps::comparesLess);
+                                       isStrictlyLess);
 
-    const auto distance1 = std::abs((frameRate.getValue() - lowerBound->getValue()));
-    const auto distance2 = std::abs((frameRate.getValue() - std::prev(lowerBound)->getValue()));
+    const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
+    const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
     return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
 }
 
@@ -920,7 +952,7 @@
     }
     if (minByPolicy == maxByPolicy) {
         // when min primary range in display manager policy is below device min turn on the timer.
-        if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) {
+        if (isApproxLess(currentPolicy->primaryRange.min, deviceMin.getFps())) {
             return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
         }
         return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
@@ -945,6 +977,17 @@
     return static_cast<int>(numPeriodsRounded);
 }
 
+bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+    if (isStrictlyLess(bigger, smaller)) {
+        return isFractionalPairOrMultiple(bigger, smaller);
+    }
+
+    const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
+    constexpr float kCoef = 1000.f / 1001.f;
+    return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
+            isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
+}
+
 void RefreshRateConfigs::dump(std::string& result) const {
     std::lock_guard lock(mLock);
     base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 21867cc..4bbdab6 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -16,31 +16,32 @@
 
 #pragma once
 
-#include <android-base/stringprintf.h>
-#include <gui/DisplayEventReceiver.h>
-
 #include <algorithm>
 #include <numeric>
 #include <optional>
 #include <type_traits>
 
+#include <android-base/stringprintf.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <scheduler/Fps.h>
+#include <scheduler/Seamlessness.h>
+
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/HWComposer.h"
-#include "Fps.h"
 #include "Scheduler/OneShotTimer.h"
 #include "Scheduler/SchedulerUtils.h"
-#include "Scheduler/Seamlessness.h"
 #include "Scheduler/StrongTyping.h"
 
 namespace android::scheduler {
 
 using namespace std::chrono_literals;
 
-enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 };
+enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 };
 
-inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateConfigEvent rhs) {
-    using T = std::underlying_type_t<RefreshRateConfigEvent>;
-    return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
+inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) {
+    using T = std::underlying_type_t<DisplayModeEvent>;
+    return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
 }
 
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -76,19 +77,15 @@
 
         // Checks whether the fps of this RefreshRate struct is within a given min and max refresh
         // rate passed in. Margin of error is applied to the boundaries for approximation.
-        bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
-            return minRefreshRate.lessThanOrEqualWithMargin(getFps()) &&
-                    getFps().lessThanOrEqualWithMargin(maxRefreshRate);
-        }
+        bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const;
 
-        bool operator!=(const RefreshRate& other) const { return mode != other.mode; }
+        bool operator==(const RefreshRate& other) const { return mode == other.mode; }
+        bool operator!=(const RefreshRate& other) const { return !operator==(other); }
 
         bool operator<(const RefreshRate& other) const {
-            return getFps().getValue() < other.getFps().getValue();
+            return isStrictlyLess(getFps(), other.getFps());
         }
 
-        bool operator==(const RefreshRate& other) const { return !(*this != other); }
-
         std::string toString() const;
         friend std::ostream& operator<<(std::ostream& os, const RefreshRate& refreshRate) {
             return os << refreshRate.toString();
@@ -105,11 +102,11 @@
             std::unordered_map<DisplayModeId, std::unique_ptr<const RefreshRate>>;
 
     struct FpsRange {
-        Fps min{0.0f};
-        Fps max{std::numeric_limits<float>::max()};
+        Fps min = Fps::fromValue(0.f);
+        Fps max = Fps::fromValue(std::numeric_limits<float>::max());
 
         bool operator==(const FpsRange& other) const {
-            return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
+            return isApproxEqual(min, other.min) && isApproxEqual(max, other.max);
         }
 
         bool operator!=(const FpsRange& other) const { return !(*this == other); }
@@ -209,6 +206,7 @@
         ExplicitExact,           // Specific refresh rate that was provided by the app with
                                  // Exact compatibility
 
+        ftl_last = ExplicitExact
     };
 
     // Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -221,7 +219,7 @@
         // Layer vote type.
         LayerVoteType vote = LayerVoteType::NoVote;
         // Layer's desired refresh rate, if applicable.
-        Fps desiredRefreshRate{0.0f};
+        Fps desiredRefreshRate;
         // If a seamless mode switch is required.
         Seamlessness seamlessness = Seamlessness::Default;
         // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
@@ -232,7 +230,7 @@
 
         bool operator==(const LayerRequirement& other) const {
             return name == other.name && vote == other.vote &&
-                    desiredRefreshRate.equalsWithMargin(other.desiredRefreshRate) &&
+                    isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) &&
                     seamlessness == other.seamlessness && weight == other.weight &&
                     focused == other.focused;
         }
@@ -247,18 +245,14 @@
         // True if the system hasn't seen any buffers posted to layers recently.
         bool idle = false;
 
-        bool operator==(const GlobalSignals& other) const {
+        bool operator==(GlobalSignals other) const {
             return touch == other.touch && idle == other.idle;
         }
     };
 
-    // Returns the refresh rate that fits best to the given layers.
-    //   layers - The layer requirements to consider.
-    //   globalSignals - global state of touch and idle
-    //   outSignalsConsidered - An output param that tells the caller whether the refresh rate was
-    //                          chosen based on touch boost and/or idle timer.
-    RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                   const GlobalSignals& globalSignals,
+    // Returns the refresh rate that best fits the given layers. outSignalsConsidered returns
+    // whether the refresh rate was chosen based on touch boost and/or idle timer.
+    RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>&, GlobalSignals,
                                    GlobalSignals* outSignalsConsidered = nullptr) const
             EXCLUDES(mLock);
 
@@ -292,9 +286,6 @@
     // Stores the current modeId the device operates at
     void setCurrentModeId(DisplayModeId) EXCLUDES(mLock);
 
-    // Returns a string that represents the layer vote type
-    static std::string layerVoteTypeString(LayerVoteType vote);
-
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
 
@@ -344,14 +335,15 @@
     // layer refresh rate.
     static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
 
+    // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
+    // for an integer t.
+    static bool isFractionalPairOrMultiple(Fps, Fps);
+
     using UidToFrameRateOverride = std::map<uid_t, Fps>;
+
     // Returns the frame rate override for each uid.
-    //
-    // @param layers list of visible layers
-    // @param displayFrameRate the display frame rate
-    // @param touch whether touch timer is active (i.e. user touched the screen recently)
-    UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
-                                                 Fps displayFrameRate, bool touch) const
+    UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>&,
+                                                 Fps displayFrameRate, GlobalSignals) const
             EXCLUDES(mLock);
 
     bool supportsKernelIdleTimer() const { return mConfig.supportKernelIdleTimer; }
@@ -404,13 +396,12 @@
             const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
             std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
 
-    std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                                        const GlobalSignals& globalSignals,
+    std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>&,
+                                                        GlobalSignals,
                                                         GlobalSignals* outSignalsConsidered) const
             REQUIRES(mLock);
 
-    RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
-                                         const GlobalSignals& globalSignals,
+    RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>&, GlobalSignals,
                                          GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
 
     // Returns the refresh rate with the highest score in the collection specified from begin
@@ -429,7 +420,11 @@
 
     // Returns the highest refresh rate according to the current policy. May change at runtime. Only
     // uses the primary range, not the app request range.
-    const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock);
+    const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
+        return getMaxRefreshRateByPolicyLocked(mCurrentRefreshRate->getModeGroup());
+    }
+
+    const RefreshRate& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
 
     // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
     // the policy.
@@ -446,6 +441,9 @@
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
                                     bool isSeamlessSwitch) const REQUIRES(mLock);
 
+    float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&,
+                                                    const RefreshRate&) const REQUIRES(mLock);
+
     void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
 
     void initializeIdleTimer();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 208a767..23ebb06 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -16,15 +16,18 @@
 
 #pragma once
 
+#include <chrono>
 #include <numeric>
 
-#include "Fps.h"
+#include <android-base/stringprintf.h>
+#include <ftl/small_map.h>
+#include <utils/Timers.h>
+
+#include <scheduler/Fps.h>
+
 #include "Scheduler/SchedulerUtils.h"
 #include "TimeStats/TimeStats.h"
 
-#include "android-base/stringprintf.h"
-#include "utils/Timers.h"
-
 namespace android::scheduler {
 
 /**
@@ -40,6 +43,7 @@
     static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
 
 public:
+    // TODO(b/185535769): Inject clock to avoid sleeping in tests.
     RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate,
                      android::hardware::graphics::composer::hal::PowerMode currentPowerMode)
           : mTimeStats(timeStats),
@@ -58,7 +62,7 @@
     // Sets config mode. If the mode has changed, it records how much time was spent in the previous
     // mode.
     void setRefreshRate(Fps currRefreshRate) {
-        if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
+        if (isApproxEqual(mCurrentRefreshRate, currRefreshRate)) {
             return;
         }
         mTimeStats.incrementRefreshRateSwitches();
@@ -66,25 +70,26 @@
         mCurrentRefreshRate = currRefreshRate;
     }
 
-    // Returns a map between human readable refresh rate and number of seconds the device spent in
-    // that mode.
-    std::unordered_map<std::string, int64_t> getTotalTimes() {
+    // Maps stringified refresh rate to total time spent in that mode.
+    using TotalTimes = ftl::SmallMap<std::string, std::chrono::milliseconds, 3>;
+
+    TotalTimes getTotalTimes() {
         // If the power mode is on, then we are probably switching between the config modes. If
         // it's not then the screen is probably off. Make sure to flush times before printing
         // them.
         flushTime();
 
-        std::unordered_map<std::string, int64_t> totalTime;
-        // Multiple configs may map to the same name, e.g. "60fps". Add the
-        // times for such configs together.
-        for (const auto& [configId, time] : mConfigModesTotalTime) {
-            totalTime[to_string(configId)] = 0;
+        TotalTimes totalTimes = ftl::init::map("ScreenOff", mScreenOffTime);
+        const auto zero = std::chrono::milliseconds::zero();
+
+        // Sum the times for modes that map to the same name, e.g. "60 Hz".
+        for (const auto& [fps, time] : mFpsTotalTimes) {
+            const auto string = to_string(fps);
+            const auto total = std::as_const(totalTimes).get(string).value_or(std::cref(zero));
+            totalTimes.emplace_or_replace(string, total.get() + time);
         }
-        for (const auto& [configId, time] : mConfigModesTotalTime) {
-            totalTime[to_string(configId)] += time;
-        }
-        totalTime["ScreenOff"] = mScreenOffTime;
-        return totalTime;
+
+        return totalTimes;
     }
 
     // Traverses through the map of config modes and returns how long they've been running in easy
@@ -102,28 +107,32 @@
     // Calculates the time that passed in ms between the last time we recorded time and the time
     // this method was called.
     void flushTime() {
-        nsecs_t currentTime = systemTime();
-        nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
-        int64_t timeElapsedMs = ns2ms(timeElapsed);
+        const nsecs_t currentTime = systemTime();
+        const nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
         mPreviousRecordedTime = currentTime;
 
+        const auto duration = std::chrono::milliseconds{ns2ms(timeElapsed)};
+        const auto zero = std::chrono::milliseconds::zero();
+
         uint32_t fps = 0;
+
         if (mCurrentPowerMode == android::hardware::graphics::composer::hal::PowerMode::ON) {
             // Normal power mode is counted under different config modes.
-            if (mConfigModesTotalTime.find(mCurrentRefreshRate) == mConfigModesTotalTime.end()) {
-                mConfigModesTotalTime[mCurrentRefreshRate] = 0;
-            }
-            mConfigModesTotalTime[mCurrentRefreshRate] += timeElapsedMs;
+            const auto total = std::as_const(mFpsTotalTimes)
+                                       .get(mCurrentRefreshRate)
+                                       .value_or(std::cref(zero));
+            mFpsTotalTimes.emplace_or_replace(mCurrentRefreshRate, total.get() + duration);
+
             fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
         } else {
-            mScreenOffTime += timeElapsedMs;
+            mScreenOffTime += duration;
         }
         mTimeStats.recordRefreshRate(fps, timeElapsed);
     }
 
     // Formats the time in milliseconds into easy to read format.
-    static std::string getDateFormatFromMs(int64_t timeMs) {
-        auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY);
+    static std::string getDateFormatFromMs(std::chrono::milliseconds time) {
+        auto [days, dayRemainderMs] = std::div(static_cast<int64_t>(time.count()), MS_PER_DAY);
         auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
         auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
         auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
@@ -138,9 +147,8 @@
     Fps mCurrentRefreshRate;
     android::hardware::graphics::composer::hal::PowerMode mCurrentPowerMode;
 
-    std::unordered_map<Fps, int64_t /* duration in ms */, std::hash<Fps>, Fps::EqualsInBuckets>
-            mConfigModesTotalTime;
-    int64_t mScreenOffTime = 0;
+    ftl::SmallMap<Fps, std::chrono::milliseconds, 2, FpsApproxEqual> mFpsTotalTimes;
+    std::chrono::milliseconds mScreenOffTime = std::chrono::milliseconds::zero();
 
     nsecs_t mPreviousRecordedTime = systemTime();
 };
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1989d57..cbe4552 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -46,11 +46,8 @@
 #include "OneShotTimer.h"
 #include "SchedulerUtils.h"
 #include "SurfaceFlingerProperties.h"
-#include "Timer.h"
-#include "VSyncDispatchTimerQueue.h"
 #include "VSyncPredictor.h"
 #include "VSyncReactor.h"
-#include "VsyncController.h"
 
 #define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
     do {                                                             \
@@ -60,74 +57,14 @@
         }                                                            \
     } while (false)
 
-using namespace std::string_literals;
+namespace android::scheduler {
 
-namespace android {
+Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
+      : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
 
-using gui::WindowInfo;
-
-namespace {
-
-std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
-    // TODO(b/144707443): Tune constants.
-    constexpr int kDefaultRate = 60;
-    constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1);
-    constexpr nsecs_t idealPeriod =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count();
-    constexpr size_t vsyncTimestampHistorySize = 20;
-    constexpr size_t minimumSamplesForPrediction = 6;
-    constexpr uint32_t discardOutlierPercent = 20;
-    return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize,
-                                                       minimumSamplesForPrediction,
-                                                       discardOutlierPercent);
-}
-
-std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) {
-    // TODO(b/144707443): Tune constants.
-    constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms;
-    constexpr std::chrono::nanoseconds timerSlack = 500us;
-    return std::make_unique<
-            scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker,
-                                                timerSlack.count(), vsyncMoveThreshold.count());
-}
-
-const char* toContentDetectionString(bool useContentDetection) {
-    return useContentDetection ? "on" : "off";
-}
-
-} // namespace
-
-class PredictedVsyncTracer {
-public:
-    PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch)
-          : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this),
-                          "PredictedVsyncTracer") {
-        scheduleRegistration();
-    }
-
-private:
-    TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
-    scheduler::VSyncCallbackRegistration mRegistration;
-
-    void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); }
-
-    void callback() {
-        mParity = !mParity;
-        scheduleRegistration();
-    }
-};
-
-Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
-                     ISchedulerCallback& callback)
-      : Scheduler(configs, callback,
-                  {.useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
-}
-
-Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
-                     ISchedulerCallback& callback, Options options)
-      : Scheduler(createVsyncSchedule(configs->supportsKernelIdleTimer()), configs, callback,
-                  createLayerHistory(), options) {
+void Scheduler::startTimers() {
     using namespace sysprop;
+    using namespace std::string_literals;
 
     if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
         // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
@@ -147,22 +84,6 @@
     }
 }
 
-Scheduler::Scheduler(VsyncSchedule schedule,
-                     const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
-                     ISchedulerCallback& schedulerCallback,
-                     std::unique_ptr<LayerHistory> layerHistory, Options options)
-      : mOptions(options),
-        mVsyncSchedule(std::move(schedule)),
-        mLayerHistory(std::move(layerHistory)),
-        mSchedulerCallback(schedulerCallback),
-        mPredictedVsyncTracer(
-                base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
-                        ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
-                        : nullptr) {
-    setRefreshRateConfigs(configs);
-    mSchedulerCallback.setVsyncEnabled(false);
-}
-
 Scheduler::~Scheduler() {
     // Ensure the OneShotTimer threads are joined before we start destroying state.
     mDisplayPowerTimer.reset();
@@ -170,27 +91,20 @@
     mRefreshRateConfigs.reset();
 }
 
-Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
-    auto clock = std::make_unique<scheduler::SystemClock>();
-    auto tracker = createVSyncTracker();
-    auto dispatch = createVSyncDispatch(*tracker);
-
-    // TODO(b/144707443): Tune constants.
-    constexpr size_t pendingFenceLimit = 20;
-    auto controller =
-            std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit,
-                                                      supportKernelTimer);
-    return {std::move(controller), std::move(tracker), std::move(dispatch)};
+void Scheduler::run() {
+    while (true) {
+        waitMessage();
+    }
 }
 
-std::unique_ptr<LayerHistory> Scheduler::createLayerHistory() {
-    return std::make_unique<scheduler::LayerHistory>();
+void Scheduler::createVsyncSchedule(FeatureFlags features) {
+    mVsyncSchedule.emplace(features);
 }
 
 std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
         const char* name, std::chrono::nanoseconds workDuration,
         std::chrono::nanoseconds readyDuration, bool traceVsync) {
-    return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration,
+    return std::make_unique<scheduler::DispSyncSource>(getVsyncDispatch(), workDuration,
                                                        readyDuration, traceVsync, name);
 }
 
@@ -206,14 +120,14 @@
     {
         const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
         if (iter != mFrameRateOverridesFromBackdoor.end()) {
-            return std::make_optional<Fps>(iter->second);
+            return iter->second;
         }
     }
 
     {
         const auto iter = mFrameRateOverridesByContent.find(uid);
         if (iter != mFrameRateOverridesByContent.end()) {
-            return std::make_optional<Fps>(iter->second);
+            return iter->second;
         }
     }
 
@@ -226,7 +140,7 @@
         return true;
     }
 
-    return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
+    return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
@@ -261,7 +175,7 @@
     };
 }
 
-Scheduler::ConnectionHandle Scheduler::createConnection(
+ConnectionHandle Scheduler::createConnection(
         const char* connectionName, frametimeline::TokenManager* tokenManager,
         std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
@@ -275,7 +189,7 @@
     return createConnection(std::move(eventThread));
 }
 
-Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
+ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
     const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
     ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
 
@@ -362,24 +276,24 @@
 
 void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
     {
-        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        std::lock_guard<std::mutex> lock(mPolicyLock);
         // Cache the last reported modes for primary display.
-        mFeatures.cachedModeChangedParams = {handle, mode};
+        mPolicy.cachedModeChangedParams = {handle, mode};
 
         // Invalidate content based refresh rate selection so it could be calculated
         // again for the new refresh rate.
-        mFeatures.contentRequirements.clear();
+        mPolicy.contentRequirements.clear();
     }
     onNonPrimaryDisplayModeChanged(handle, mode);
 }
 
 void Scheduler::dispatchCachedReportedMode() {
     // Check optional fields first.
-    if (!mFeatures.mode) {
+    if (!mPolicy.mode) {
         ALOGW("No mode ID found, not dispatching cached mode.");
         return;
     }
-    if (!mFeatures.cachedModeChangedParams.has_value()) {
+    if (!mPolicy.cachedModeChangedParams) {
         ALOGW("No mode changed params found, not dispatching cached mode.");
         return;
     }
@@ -388,18 +302,18 @@
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
     if (std::scoped_lock lock(mRefreshRateConfigsLock);
-        mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mFeatures.mode) {
+        mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mPolicy.mode) {
         return;
     }
 
     // If there is no change from cached mode, there is no need to dispatch an event
-    if (mFeatures.mode == mFeatures.cachedModeChangedParams->mode) {
+    if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) {
         return;
     }
 
-    mFeatures.cachedModeChangedParams->mode = mFeatures.mode;
-    onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle,
-                                   mFeatures.cachedModeChangedParams->mode);
+    mPolicy.cachedModeChangedParams->mode = mPolicy.mode;
+    onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
+                                   mPolicy.cachedModeChangedParams->mode);
 }
 
 void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
@@ -440,12 +354,12 @@
 }
 
 DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
-    const auto vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
-    const auto vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
+    const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now);
+    const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod();
     return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
 }
 
-Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
+ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
     if (mInjectVSyncs == enable) {
         return {};
     }
@@ -486,7 +400,7 @@
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
-        mVsyncSchedule.tracker->resetModel();
+        mVsyncSchedule->getTracker().resetModel();
         mSchedulerCallback.setVsyncEnabled(true);
         mPrimaryHWVsyncEnabled = true;
     }
@@ -539,10 +453,10 @@
 
 void Scheduler::setVsyncPeriod(nsecs_t period) {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
-    mVsyncSchedule.controller->startPeriodTransition(period);
+    mVsyncSchedule->getController().startPeriodTransition(period);
 
     if (!mPrimaryHWVsyncEnabled) {
-        mVsyncSchedule.tracker->resetModel();
+        mVsyncSchedule->getTracker().resetModel();
         mSchedulerCallback.setVsyncEnabled(true);
         mPrimaryHWVsyncEnabled = true;
     }
@@ -555,8 +469,9 @@
     { // Scope for the lock
         std::lock_guard<std::mutex> lock(mHWVsyncLock);
         if (mPrimaryHWVsyncEnabled) {
-            needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
-                                                                          periodFlushed);
+            needsHwVsync =
+                    mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
+                                                                        periodFlushed);
         }
     }
 
@@ -567,24 +482,23 @@
     }
 }
 
-void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
-    if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
+void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) {
+    if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) {
         enableHardwareVsync();
     } else {
         disableHardwareVsync(false);
     }
 }
 
-void Scheduler::setIgnorePresentFences(bool ignore) {
-    mVsyncSchedule.controller->setIgnorePresentFences(ignore);
-}
-
 void Scheduler::registerLayer(Layer* layer) {
+    using WindowType = gui::WindowInfo::Type;
+
     scheduler::LayerHistory::LayerVoteType voteType;
 
-    if (!mOptions.useContentDetection || layer->getWindowType() == WindowInfo::Type::STATUS_BAR) {
+    if (!mFeatures.test(Feature::kContentDetection) ||
+        layer->getWindowType() == WindowType::STATUS_BAR) {
         voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
-    } else if (layer->getWindowType() == WindowInfo::Type::WALLPAPER) {
+    } else if (layer->getWindowType() == WindowType::WALLPAPER) {
         // Running Wallpaper at Min is considered as part of content detection.
         voteType = scheduler::LayerHistory::LayerVoteType::Min;
     } else {
@@ -594,11 +508,11 @@
     // If the content detection feature is off, we still keep the layer history,
     // since we use it for other features (like Frame Rate API), so layers
     // still need to be registered.
-    mLayerHistory->registerLayer(layer, voteType);
+    mLayerHistory.registerLayer(layer, voteType);
 }
 
 void Scheduler::deregisterLayer(Layer* layer) {
-    mLayerHistory->deregisterLayer(layer);
+    mLayerHistory.deregisterLayer(layer);
 }
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
@@ -608,11 +522,11 @@
         if (!mRefreshRateConfigs->canSwitch()) return;
     }
 
-    mLayerHistory->record(layer, presentTime, systemTime(), updateType);
+    mLayerHistory.record(layer, presentTime, systemTime(), updateType);
 }
 
 void Scheduler::setModeChangePending(bool pending) {
-    mLayerHistory->setModeChangePending(pending);
+    mLayerHistory.setModeChangePending(pending);
 }
 
 void Scheduler::chooseRefreshRateForContent() {
@@ -625,19 +539,19 @@
 
     const auto refreshRateConfigs = holdRefreshRateConfigs();
     scheduler::LayerHistory::Summary summary =
-            mLayerHistory->summarize(*refreshRateConfigs, systemTime());
+            mLayerHistory.summarize(*refreshRateConfigs, systemTime());
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     DisplayModePtr newMode;
     bool frameRateChanged;
     bool frameRateOverridesChanged;
     {
-        std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        mFeatures.contentRequirements = summary;
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.contentRequirements = summary;
 
         newMode = calculateRefreshRateModeId(&consideredSignals);
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
 
-        if (mFeatures.mode == newMode) {
+        if (mPolicy.mode == newMode) {
             // We don't need to change the display mode, but we might need to send an event
             // about a mode change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
@@ -645,15 +559,16 @@
             }
             frameRateChanged = false;
         } else {
-            mFeatures.mode = newMode;
+            mPolicy.mode = newMode;
             frameRateChanged = true;
         }
     }
     if (frameRateChanged) {
-        auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+        const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                             consideredSignals.idle ? ModeEvent::None
-                                                                    : ModeEvent::Changed);
+                                             consideredSignals.idle ? DisplayModeEvent::None
+                                                                    : DisplayModeEvent::Changed);
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -665,7 +580,7 @@
     mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false);
 }
 
-void Scheduler::notifyTouchEvent() {
+void Scheduler::onTouchHint() {
     if (mTouchTimer) {
         mTouchTimer->reset();
 
@@ -676,8 +591,8 @@
 
 void Scheduler::setDisplayPowerState(bool normal) {
     {
-        std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        mFeatures.isDisplayPowerStateNormal = normal;
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.isDisplayPowerStateNormal = normal;
     }
 
     if (mDisplayPowerTimer) {
@@ -686,7 +601,7 @@
 
     // Display Power event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    mLayerHistory->clear();
+    mLayerHistory.clear();
 }
 
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -699,15 +614,16 @@
         return mRefreshRateConfigs->getCurrentRefreshRate();
     }();
 
-    constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER{65.0f};
-    if (state == TimerState::Reset &&
-        refreshRate.getFps().greaterThanWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
+    constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
+    using namespace fps_approx_ops;
+
+    if (state == TimerState::Reset && refreshRate.getFps() > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
         resyncToHardwareVsync(true /* makeAvailable */, refreshRate.getVsyncPeriod());
     } else if (state == TimerState::Expired &&
-               refreshRate.getFps().lessThanOrEqualWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
+               refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // Disable HW VSYNC if the timer expired, as we don't need it enabled if
         // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
         // need to update the VsyncController model anyway.
@@ -718,7 +634,7 @@
 }
 
 void Scheduler::idleTimerCallback(TimerState state) {
-    handleTimerStateChanged(&mFeatures.idleTimer, state);
+    handleTimerStateChanged(&mPolicy.idleTimer, state);
     ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
 }
 
@@ -728,14 +644,14 @@
     // Clear layer history to get fresh FPS detection.
     // NOTE: Instead of checking all the layers, we should be checking the layer
     // that is currently on top. b/142507166 will give us this capability.
-    if (handleTimerStateChanged(&mFeatures.touch, touch)) {
-        mLayerHistory->clear();
+    if (handleTimerStateChanged(&mPolicy.touch, touch)) {
+        mLayerHistory.clear();
     }
     ATRACE_INT("TouchState", static_cast<int>(touch));
 }
 
 void Scheduler::displayPowerTimerCallback(TimerState state) {
-    handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
+    handleTimerStateChanged(&mPolicy.displayPowerTimer, state);
     ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
 }
 
@@ -745,8 +661,8 @@
     StringAppendF(&result, "+  Touch timer: %s\n",
                   mTouchTimer ? mTouchTimer->dump().c_str() : "off");
     StringAppendF(&result, "+  Content detection: %s %s\n\n",
-                  toContentDetectionString(mOptions.useContentDetection),
-                  mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+                  mFeatures.test(Feature::kContentDetection) ? "on" : "off",
+                  mLayerHistory.dump().c_str());
 
     {
         std::lock_guard lock(mFrameRateOverridesLock);
@@ -771,13 +687,8 @@
     }
 }
 
-void Scheduler::dumpVsync(std::string& s) const {
-    using base::StringAppendF;
-
-    StringAppendF(&s, "VSyncReactor:\n");
-    mVsyncSchedule.controller->dump(s);
-    StringAppendF(&s, "VSyncDispatch:\n");
-    mVsyncSchedule.dispatch->dump(s);
+void Scheduler::dumpVsync(std::string& out) const {
+    mVsyncSchedule->dump(out);
 }
 
 bool Scheduler::updateFrameRateOverrides(
@@ -789,14 +700,13 @@
 
     if (!consideredSignals.idle) {
         const auto frameRateOverrides =
-                refreshRateConfigs->getFrameRateOverrides(mFeatures.contentRequirements,
-                                                          displayRefreshRate,
-                                                          consideredSignals.touch);
+                refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
+                                                          displayRefreshRate, consideredSignals);
         std::lock_guard lock(mFrameRateOverridesLock);
         if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
                         frameRateOverrides.begin(), frameRateOverrides.end(),
-                        [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
-                            return a.first == b.first && a.second.equalsWithMargin(b.second);
+                        [](const auto& lhs, const auto& rhs) {
+                            return lhs.first == rhs.first && isApproxEqual(lhs.second, rhs.second);
                         })) {
             mFrameRateOverridesByContent = frameRateOverrides;
             return true;
@@ -813,31 +723,30 @@
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     const auto refreshRateConfigs = holdRefreshRateConfigs();
     {
-        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        std::lock_guard<std::mutex> lock(mPolicyLock);
         if (*currentState == newState) {
             return false;
         }
         *currentState = newState;
         newMode = calculateRefreshRateModeId(&consideredSignals);
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
-        if (mFeatures.mode == newMode) {
+        if (mPolicy.mode == newMode) {
             // We don't need to change the display mode, but we might need to send an event
             // about a mode change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedMode();
             }
         } else {
-            mFeatures.mode = newMode;
+            mPolicy.mode = newMode;
             refreshRateChanged = true;
         }
     }
     if (refreshRateChanged) {
-        const RefreshRate& newRefreshRate =
-                refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+        const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
 
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                             consideredSignals.idle ? ModeEvent::None
-                                                                    : ModeEvent::Changed);
+                                             consideredSignals.idle ? DisplayModeEvent::None
+                                                                    : DisplayModeEvent::Changed);
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -854,32 +763,31 @@
     // If Display Power is not in normal operation we want to be in performance mode. When coming
     // back to normal mode, a grace period is given with DisplayPowerTimer.
     if (mDisplayPowerTimer &&
-        (!mFeatures.isDisplayPowerStateNormal ||
-         mFeatures.displayPowerTimer == TimerState::Reset)) {
+        (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
         return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode();
     }
 
-    const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
-    const bool idle = mFeatures.idleTimer == TimerState::Expired;
+    const bool touchActive = mTouchTimer && mPolicy.touch == TouchState::Active;
+    const bool idle = mPolicy.idleTimer == TimerState::Expired;
 
     return refreshRateConfigs
-            ->getBestRefreshRate(mFeatures.contentRequirements,
-                                 {.touch = touchActive, .idle = idle}, consideredSignals)
+            ->getBestRefreshRate(mPolicy.contentRequirements, {.touch = touchActive, .idle = idle},
+                                 consideredSignals)
             .getMode();
 }
 
 DisplayModePtr Scheduler::getPreferredDisplayMode() {
-    std::lock_guard<std::mutex> lock(mFeatureStateLock);
+    std::lock_guard<std::mutex> lock(mPolicyLock);
     // Make sure that the default mode ID is first updated, before returned.
-    if (mFeatures.mode) {
-        mFeatures.mode = calculateRefreshRateModeId();
+    if (mPolicy.mode) {
+        mPolicy.mode = calculateRefreshRateModeId();
     }
-    return mFeatures.mode;
+    return mPolicy.mode;
 }
 
 void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
     if (timeline.refreshRequired) {
-        mSchedulerCallback.repaintEverythingForHWC();
+        mSchedulerCallback.scheduleComposite(FrameHint::kNone);
     }
 
     std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
@@ -891,27 +799,27 @@
     }
 }
 
-void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
-    bool callRepaint = false;
-    {
+void Scheduler::onPostComposition(nsecs_t presentTime) {
+    const bool recomposite = [=] {
         std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
         if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
-            if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
-                mLastVsyncPeriodChangeTimeline->refreshRequired = false;
-            } else {
-                // We need to send another refresh as refreshTimeNanos is still in the future
-                callRepaint = true;
+            if (presentTime < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) {
+                // We need to composite again as refreshTimeNanos is still in the future.
+                return true;
             }
-        }
-    }
 
-    if (callRepaint) {
-        mSchedulerCallback.repaintEverythingForHWC();
+            mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+        }
+        return false;
+    }();
+
+    if (recomposite) {
+        mSchedulerCallback.scheduleComposite(FrameHint::kNone);
     }
 }
 
 void Scheduler::onActiveDisplayAreaChanged(uint32_t displayArea) {
-    mLayerHistory->setDisplayArea(displayArea);
+    mLayerHistory.setDisplayArea(displayArea);
 }
 
 void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
@@ -921,7 +829,8 @@
 
     std::lock_guard lock(mFrameRateOverridesLock);
     if (frameRateOverride.frameRateHz != 0.f) {
-        mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+        mFrameRateOverridesFromBackdoor[frameRateOverride.uid] =
+                Fps::fromValue(frameRateOverride.frameRateHz);
     } else {
         mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
     }
@@ -930,8 +839,8 @@
 std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
         nsecs_t expectedPresentTime) const {
     const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
-    const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule.tracker->currentPeriod());
+    const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod());
     return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
 }
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index bbbbca5..818f1ed 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -18,6 +18,7 @@
 
 #include <atomic>
 #include <functional>
+#include <future>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -30,36 +31,37 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
+#include <scheduler/Features.h>
+
 #include "EventThread.h"
 #include "LayerHistory.h"
+#include "MessageQueue.h"
 #include "OneShotTimer.h"
 #include "RefreshRateConfigs.h"
 #include "SchedulerUtils.h"
+#include "VsyncSchedule.h"
 
 namespace android {
 
-using namespace std::chrono_literals;
-using scheduler::LayerHistory;
-
 class FenceTime;
 class InjectVSyncSource;
-class PredictedVsyncTracer;
-
-namespace scheduler {
-class VsyncController;
-class VSyncDispatch;
-class VSyncTracker;
-} // namespace scheduler
 
 namespace frametimeline {
 class TokenManager;
 } // namespace frametimeline
 
+namespace scheduler {
+
 struct ISchedulerCallback {
+    // Indicates frame activity, i.e. whether commit and/or composite is taking place.
+    enum class FrameHint { kNone, kActive };
+
+    using RefreshRate = RefreshRateConfigs::RefreshRate;
+    using DisplayModeEvent = scheduler::DisplayModeEvent;
+
+    virtual void scheduleComposite(FrameHint) = 0;
     virtual void setVsyncEnabled(bool) = 0;
-    virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
-                                   scheduler::RefreshRateConfigEvent) = 0;
-    virtual void repaintEverythingForHWC() = 0;
+    virtual void changeRefreshRate(const RefreshRate&, DisplayModeEvent) = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
 
@@ -67,15 +69,33 @@
     ~ISchedulerCallback() = default;
 };
 
-class Scheduler {
-public:
-    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-    using ModeEvent = scheduler::RefreshRateConfigEvent;
+class Scheduler : impl::MessageQueue {
+    using Impl = impl::MessageQueue;
 
-    Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&);
+public:
+    Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
     ~Scheduler();
 
-    using ConnectionHandle = scheduler::ConnectionHandle;
+    void createVsyncSchedule(FeatureFlags);
+    void startTimers();
+    void run();
+
+    using Impl::initVsync;
+    using Impl::setInjector;
+
+    using Impl::getScheduledFrameTime;
+    using Impl::setDuration;
+
+    using Impl::scheduleFrame;
+
+    // Schedule an asynchronous or synchronous task on the main thread.
+    template <typename F, typename T = std::invoke_result_t<F>>
+    [[nodiscard]] std::future<T> schedule(F&& f) {
+        auto [task, future] = makeTask(std::move(f));
+        postMessage(std::move(task));
+        return std::move(future);
+    }
+
     ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
                                       std::chrono::nanoseconds workDuration,
                                       std::chrono::nanoseconds readyDuration,
@@ -87,7 +107,7 @@
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mFeatureStateLock);
+    void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock);
     void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
@@ -120,8 +140,7 @@
     // VsyncController detected that the vsync period changed, and false otherwise.
     void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                          bool* periodFlushed);
-    void addPresentFence(const std::shared_ptr<FenceTime>&);
-    void setIgnorePresentFences(bool ignore);
+    void addPresentFence(std::shared_ptr<FenceTime>);
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
@@ -135,12 +154,12 @@
 
     void resetIdleTimer();
 
-    // Function that resets the touch timer.
-    void notifyTouchEvent();
+    // Indicates that touch interaction is taking place.
+    void onTouchHint();
 
     void setDisplayPowerState(bool normal);
 
-    scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+    VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
 
     // Returns true if a given vsync timestamp is considered valid vsync
     // for a given uid
@@ -159,8 +178,8 @@
     // Notifies the scheduler about a refresh rate timeline change.
     void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
 
-    // Notifies the scheduler when the display was refreshed
-    void onDisplayRefreshed(nsecs_t timestamp);
+    // Notifies the scheduler post composition.
+    void onPostComposition(nsecs_t presentTime);
 
     // Notifies the scheduler when the display size has changed. Called from SF's main thread
     void onActiveDisplayAreaChanged(uint32_t displayArea);
@@ -179,7 +198,7 @@
     std::optional<Fps> getFrameRateOverride(uid_t uid) const
             EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock);
 
-    void setRefreshRateConfigs(std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs)
+    void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> refreshRateConfigs)
             EXCLUDES(mRefreshRateConfigsLock) {
         // We need to stop the idle timer on the previous RefreshRateConfigs instance
         // and cleanup the scheduler's state before we switch to the other RefreshRateConfigs.
@@ -188,8 +207,8 @@
             if (mRefreshRateConfigs) mRefreshRateConfigs->stopIdleTimer();
         }
         {
-            std::scoped_lock lock(mFeatureStateLock);
-            mFeatures = {};
+            std::scoped_lock lock(mPolicyLock);
+            mPolicy = {};
         }
         {
             std::scoped_lock lock(mRefreshRateConfigsLock);
@@ -214,36 +233,20 @@
         return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
     }
 
+    // Returns the framerate of the layer with the given sequence ID
+    float getLayerFramerate(nsecs_t now, int32_t id) const {
+        return mLayerHistory.getLayerFramerate(now, id);
+    }
+
 private:
     friend class TestableScheduler;
 
-    // In order to make sure that the features don't override themselves, we need a state machine
-    // to keep track which feature requested the config change.
+    using FrameHint = ISchedulerCallback::FrameHint;
+
     enum class ContentDetectionState { Off, On };
     enum class TimerState { Reset, Expired };
     enum class TouchState { Inactive, Active };
 
-    struct Options {
-        // Whether to use content detection at all.
-        bool useContentDetection;
-    };
-
-    struct VsyncSchedule {
-        std::unique_ptr<scheduler::VsyncController> controller;
-        std::unique_ptr<scheduler::VSyncTracker> tracker;
-        std::unique_ptr<scheduler::VSyncDispatch> dispatch;
-    };
-
-    // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers.
-    Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&, Options);
-
-    // Used by tests to inject mocks.
-    Scheduler(VsyncSchedule, const std::shared_ptr<scheduler::RefreshRateConfigs>&,
-              ISchedulerCallback&, std::unique_ptr<LayerHistory>, Options);
-
-    static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
-    static std::unique_ptr<LayerHistory> createLayerHistory();
-
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
     sp<EventThreadConnection> createConnectionInternal(
@@ -265,19 +268,17 @@
     // selection were initialized, prioritizes them, and calculates the DisplayModeId
     // for the suggested refresh rate.
     DisplayModePtr calculateRefreshRateModeId(
-            scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
-            REQUIRES(mFeatureStateLock);
+            RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mPolicyLock);
 
-    void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock) EXCLUDES(mRefreshRateConfigsLock);
-    bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
-                                  Fps displayRefreshRate) REQUIRES(mFeatureStateLock)
-            EXCLUDES(mFrameRateOverridesLock);
+    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
+    bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate)
+            REQUIRES(mPolicyLock) EXCLUDES(mFrameRateOverridesLock);
 
     impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
             EXCLUDES(mRefreshRateConfigsLock);
     impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
-    std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const
+    std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
             EXCLUDES(mRefreshRateConfigsLock) {
         std::scoped_lock lock(mRefreshRateConfigsLock);
         return mRefreshRateConfigs;
@@ -303,66 +304,63 @@
 
     std::atomic<nsecs_t> mLastResyncTime = 0;
 
-    const Options mOptions;
-    VsyncSchedule mVsyncSchedule;
+    const FeatureFlags mFeatures;
+    std::optional<VsyncSchedule> mVsyncSchedule;
 
     // Used to choose refresh rate if content detection is enabled.
-    std::unique_ptr<LayerHistory> mLayerHistory;
+    LayerHistory mLayerHistory;
 
     // Timer used to monitor touch events.
-    std::optional<scheduler::OneShotTimer> mTouchTimer;
+    std::optional<OneShotTimer> mTouchTimer;
     // Timer used to monitor display power mode.
-    std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
+    std::optional<OneShotTimer> mDisplayPowerTimer;
 
     ISchedulerCallback& mSchedulerCallback;
 
-    // In order to make sure that the features don't override themselves, we need a state machine
-    // to keep track which feature requested the config change.
-    mutable std::mutex mFeatureStateLock;
+    mutable std::mutex mPolicyLock;
 
     struct {
+        // Policy for choosing the display mode.
+        LayerHistory::Summary contentRequirements;
         TimerState idleTimer = TimerState::Reset;
         TouchState touch = TouchState::Inactive;
         TimerState displayPowerTimer = TimerState::Expired;
-
-        DisplayModePtr mode;
-        LayerHistory::Summary contentRequirements;
-
         bool isDisplayPowerStateNormal = true;
 
-        // Used to cache the last parameters of onPrimaryDisplayModeChanged
+        // Chosen display mode.
+        DisplayModePtr mode;
+
         struct ModeChangedParams {
             ConnectionHandle handle;
             DisplayModePtr mode;
         };
 
+        // Parameters for latest dispatch of mode change event.
         std::optional<ModeChangedParams> cachedModeChangedParams;
-    } mFeatures GUARDED_BY(mFeatureStateLock);
+    } mPolicy GUARDED_BY(mPolicyLock);
 
     mutable std::mutex mRefreshRateConfigsLock;
-    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs
-            GUARDED_BY(mRefreshRateConfigsLock);
+    std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock);
 
     std::mutex mVsyncTimelineLock;
     std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
             GUARDED_BY(mVsyncTimelineLock);
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
-    const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
-
     // The frame rate override lists need their own mutex as they are being read
     // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
     mutable std::mutex mFrameRateOverridesLock;
 
     // mappings between a UID and a preferred refresh rate that this app would
     // run at.
-    scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
+    RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
             GUARDED_BY(mFrameRateOverridesLock);
-    scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
+    RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
             GUARDED_BY(mFrameRateOverridesLock);
 
     // Keeps track of whether the screen is acquired for debug
     std::atomic<bool> mScreenAcquired = false;
 };
 
+} // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index c9c2d84..68f9321 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -17,14 +17,18 @@
 #undef LOG_TAG
 #define LOG_TAG "SchedulerTimer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <android-base/stringprintf.h>
-#include <log/log.h>
+
+#include <chrono>
+#include <cstdint>
+
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <sys/unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <log/log.h>
 #include <utils/Trace.h>
-#include <chrono>
-#include <cstdint>
 
 #include "SchedulerUtils.h"
 #include "Timer.h"
@@ -47,13 +51,25 @@
 }
 
 void Timer::reset() {
-    cleanup();
-    mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
-    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
-    if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
-        ALOGE("could not create TimerDispatch mPipes");
-        return;
-    };
+    std::function<void()> cb;
+    {
+        std::lock_guard lock(mMutex);
+        if (mExpectingCallback && mCallback) {
+            cb = mCallback;
+        }
+
+        cleanup();
+        mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+        mEpollFd = epoll_create1(EPOLL_CLOEXEC);
+        if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
+            ALOGE("could not create TimerDispatch mPipes");
+        }
+    }
+    if (cb) {
+        setDebugState(DebugState::InCallback);
+        cb();
+        setDebugState(DebugState::Running);
+    }
     setDebugState(DebugState::Reset);
 }
 
@@ -77,6 +93,8 @@
         close(mPipes[kWritePipe]);
         mPipes[kWritePipe] = -1;
     }
+    mExpectingCallback = false;
+    mCallback = {};
 }
 
 void Timer::endDispatch() {
@@ -95,6 +113,7 @@
             std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
 
     mCallback = cb;
+    mExpectingCallback = true;
 
     struct itimerspec old_timer;
     struct itimerspec new_timer {
@@ -194,6 +213,7 @@
                 {
                     std::lock_guard lock(mMutex);
                     cb = mCallback;
+                    mExpectingCallback = false;
                 }
                 if (cb) {
                     setDebugState(DebugState::InCallback);
@@ -215,26 +235,9 @@
     mDebugState = state;
 }
 
-const char* Timer::strDebugState(DebugState state) const {
-    switch (state) {
-        case DebugState::Reset:
-            return "Reset";
-        case DebugState::Running:
-            return "Running";
-        case DebugState::Waiting:
-            return "Waiting";
-        case DebugState::Reading:
-            return "Reading";
-        case DebugState::InCallback:
-            return "InCallback";
-        case DebugState::Terminated:
-            return "Terminated";
-    }
-}
-
 void Timer::dump(std::string& result) const {
     std::lock_guard lock(mMutex);
-    StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
+    StringAppendF(&result, "\t\tDebugState: %s\n", ftl::enum_string(mDebugState).c_str());
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
index 69ce079..eb65954 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -36,15 +36,28 @@
     void alarmCancel() final;
     void dump(std::string& result) const final;
 
+protected:
+    // For unit testing
+    int mEpollFd = -1;
+
 private:
-    enum class DebugState { Reset, Running, Waiting, Reading, InCallback, Terminated };
-    void reset();
-    void cleanup();
+    enum class DebugState {
+        Reset,
+        Running,
+        Waiting,
+        Reading,
+        InCallback,
+        Terminated,
+
+        ftl_last = Terminated
+    };
+
+    void reset() EXCLUDES(mMutex);
+    void cleanup() REQUIRES(mMutex);
     void setDebugState(DebugState state) EXCLUDES(mMutex);
-    const char* strDebugState(DebugState state) const;
 
     int mTimerFd = -1;
-    int mEpollFd = -1;
+
     std::array<int, 2> mPipes = {-1, -1};
 
     std::thread mDispatchThread;
@@ -54,6 +67,7 @@
 
     mutable std::mutex mMutex;
     std::function<void()> mCallback GUARDED_BY(mMutex);
+    bool mExpectingCallback GUARDED_BY(mMutex) = false;
     DebugState mDebugState GUARDED_BY(mMutex);
 };
 
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index ee973f7..1c9de1c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -47,7 +47,7 @@
 
 VSyncReactor::~VSyncReactor() = default;
 
-bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) {
+bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) {
     if (!fence) {
         return false;
     }
@@ -80,7 +80,7 @@
         if (mPendingLimit == mUnfiredFences.size()) {
             mUnfiredFences.erase(mUnfiredFences.begin());
         }
-        mUnfiredFences.push_back(fence);
+        mUnfiredFences.push_back(std::move(fence));
     } else {
         timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
     }
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 449d4c3..a9d536b 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -37,7 +37,7 @@
                  bool supportKernelIdleTimer);
     ~VSyncReactor();
 
-    bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final;
+    bool addPresentFence(std::shared_ptr<FenceTime>) final;
     void setIgnorePresentFences(bool ignore) final;
 
     void startPeriodTransition(nsecs_t period) final;
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 95750ad..76315d2 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -17,7 +17,9 @@
 #pragma once
 
 #include <utils/Timers.h>
-#include "Fps.h"
+
+#include <scheduler/Fps.h>
+
 #include "VSyncDispatch.h"
 
 namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 43e0297..ff31651 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -48,14 +48,12 @@
 }
 
 PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
-    const auto iter = mOffsetsCache.find(fps);
-    if (iter != mOffsetsCache.end()) {
-        return iter->second;
+    if (const auto offsets = mOffsetsCache.get(fps)) {
+        return offsets->get();
     }
 
-    const auto offset = constructOffsets(fps.getPeriodNsecs());
-    mOffsetsCache[fps] = offset;
-    return offset;
+    const auto [it, _] = mOffsetsCache.try_emplace(fps, constructOffsets(fps.getPeriodNsecs()));
+    return it->second;
 }
 
 void VsyncConfiguration::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index 3e53b3f..02ebd70 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -17,13 +17,14 @@
 #pragma once
 
 #include <mutex>
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
+#include <optional>
+#include <string>
 
+#include <ftl/small_map.h>
 #include <utils/Timers.h>
 
-#include "Fps.h"
+#include <scheduler/Fps.h>
+
 #include "VsyncModulator.h"
 
 namespace android::scheduler {
@@ -88,9 +89,8 @@
 
     VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
 
-    mutable std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets>
-            mOffsetsCache GUARDED_BY(mLock);
-    std::atomic<Fps> mRefreshRateFps GUARDED_BY(mLock);
+    mutable ftl::SmallMap<Fps, VsyncConfigSet, 2, FpsApproxEqual> mOffsetsCache GUARDED_BY(mLock);
+    Fps mRefreshRateFps GUARDED_BY(mLock);
     mutable std::mutex mLock;
 };
 
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 0f0df22..59f6537 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -17,19 +17,15 @@
 #pragma once
 
 #include <cstddef>
+#include <memory>
 
+#include <ui/FenceTime.h>
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
-#include <ui/FenceTime.h>
-
-#include <memory>
-
 namespace android::scheduler {
 
-class FenceTime;
-
 class VsyncController {
 public:
     virtual ~VsyncController();
@@ -43,7 +39,7 @@
      *                      an accurate prediction,
      *                      False otherwise
      */
-    virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0;
+    virtual bool addPresentFence(std::shared_ptr<FenceTime>) = 0;
 
     /*
      * Adds a hw sync timestamp to the model. The controller will use the timestamp
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index b2b0451..2000c54 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -25,6 +25,8 @@
 #include <binder/IBinder.h>
 #include <utils/Timers.h>
 
+#include "../WpHash.h"
+
 namespace android::scheduler {
 
 // State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
@@ -124,12 +126,6 @@
     using Schedule = TransactionSchedule;
     std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
 
-    struct WpHash {
-        size_t operator()(const wp<IBinder>& p) const {
-            return std::hash<IBinder*>()(p.unsafe_get());
-        }
-    };
-
     std::unordered_set<wp<IBinder>, WpHash> mEarlyWakeupRequests GUARDED_BY(mMutex);
     std::atomic<bool> mRefreshRateChangePending = false;
 
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
new file mode 100644
index 0000000..77d1223
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <scheduler/Fps.h>
+
+#include "VsyncSchedule.h"
+
+#include "Timer.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncPredictor.h"
+#include "VSyncReactor.h"
+
+#include "../TracedOrdinal.h"
+
+namespace android::scheduler {
+
+class VsyncSchedule::PredictedVsyncTracer {
+    // Invoked from the thread of the VsyncDispatch owned by this VsyncSchedule.
+    constexpr auto makeVsyncCallback() {
+        return [this](nsecs_t, nsecs_t, nsecs_t) {
+            mParity = !mParity;
+            schedule();
+        };
+    }
+
+public:
+    explicit PredictedVsyncTracer(VsyncDispatch& dispatch)
+          : mRegistration(dispatch, makeVsyncCallback(), __func__) {
+        schedule();
+    }
+
+private:
+    void schedule() { mRegistration.schedule({0, 0, 0}); }
+
+    TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+    VSyncCallbackRegistration mRegistration;
+};
+
+VsyncSchedule::VsyncSchedule(FeatureFlags features)
+      : mTracker(createTracker()),
+        mDispatch(createDispatch(*mTracker)),
+        mController(createController(*mTracker, features)) {
+    if (features.test(Feature::kTracePredictedVsync)) {
+        mTracer = std::make_unique<PredictedVsyncTracer>(*mDispatch);
+    }
+}
+
+VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, ControllerPtr controller)
+      : mTracker(std::move(tracker)),
+        mDispatch(std::move(dispatch)),
+        mController(std::move(controller)) {}
+
+VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default;
+VsyncSchedule::~VsyncSchedule() = default;
+
+void VsyncSchedule::dump(std::string& out) const {
+    out.append("VsyncController:\n");
+    mController->dump(out);
+
+    out.append("VsyncDispatch:\n");
+    mDispatch->dump(out);
+}
+
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker() {
+    // TODO(b/144707443): Tune constants.
+    constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
+    constexpr size_t kHistorySize = 20;
+    constexpr size_t kMinSamplesForPrediction = 6;
+    constexpr uint32_t kDiscardOutlierPercent = 20;
+
+    return std::make_unique<VSyncPredictor>(kInitialPeriod, kHistorySize, kMinSamplesForPrediction,
+                                            kDiscardOutlierPercent);
+}
+
+VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& tracker) {
+    using namespace std::chrono_literals;
+
+    // TODO(b/144707443): Tune constants.
+    constexpr std::chrono::nanoseconds kGroupDispatchWithin = 500us;
+    constexpr std::chrono::nanoseconds kSnapToSameVsyncWithin = 3ms;
+
+    return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+                                                     kGroupDispatchWithin.count(),
+                                                     kSnapToSameVsyncWithin.count());
+}
+
+VsyncSchedule::ControllerPtr VsyncSchedule::createController(VsyncTracker& tracker,
+                                                             FeatureFlags features) {
+    // TODO(b/144707443): Tune constants.
+    constexpr size_t kMaxPendingFences = 20;
+    const bool hasKernelIdleTimer = features.test(Feature::kKernelIdleTimer);
+
+    auto reactor = std::make_unique<VSyncReactor>(std::make_unique<SystemClock>(), tracker,
+                                                  kMaxPendingFences, hasKernelIdleTimer);
+
+    reactor->setIgnorePresentFences(!features.test(Feature::kPresentFences));
+    return reactor;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
new file mode 100644
index 0000000..0d9b114
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <scheduler/Features.h>
+
+namespace android::scheduler {
+
+// TODO(b/185535769): Rename classes, and remove aliases.
+class VSyncDispatch;
+class VSyncTracker;
+
+class VsyncController;
+using VsyncDispatch = VSyncDispatch;
+using VsyncTracker = VSyncTracker;
+
+// Schedule that synchronizes to hardware VSYNC of a physical display.
+class VsyncSchedule {
+public:
+    explicit VsyncSchedule(FeatureFlags);
+    VsyncSchedule(VsyncSchedule&&);
+    ~VsyncSchedule();
+
+    // TODO(b/185535769): Hide behind API.
+    const VsyncTracker& getTracker() const { return *mTracker; }
+    VsyncTracker& getTracker() { return *mTracker; }
+    VsyncController& getController() { return *mController; }
+
+    // TODO(b/185535769): Remove once VsyncSchedule owns all registrations.
+    VsyncDispatch& getDispatch() { return *mDispatch; }
+
+    void dump(std::string&) const;
+
+private:
+    friend class TestableScheduler;
+
+    using TrackerPtr = std::unique_ptr<VsyncTracker>;
+    using DispatchPtr = std::unique_ptr<VsyncDispatch>;
+    using ControllerPtr = std::unique_ptr<VsyncController>;
+
+    // For tests.
+    VsyncSchedule(TrackerPtr, DispatchPtr, ControllerPtr);
+
+    static TrackerPtr createTracker();
+    static DispatchPtr createDispatch(VsyncTracker&);
+    static ControllerPtr createController(VsyncTracker&, FeatureFlags);
+
+    class PredictedVsyncTracer;
+    using TracerPtr = std::unique_ptr<PredictedVsyncTracer>;
+
+    // Effectively const except in move constructor.
+    TrackerPtr mTracker;
+    DispatchPtr mDispatch;
+    ControllerPtr mController;
+    TracerPtr mTracer;
+};
+
+} // namespace android::scheduler
diff --git a/libs/ui/Size.cpp b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
similarity index 60%
copy from libs/ui/Size.cpp
copy to services/surfaceflinger/Scheduler/include/scheduler/Features.h
index d2996d1..0e96678 100644
--- a/libs/ui/Size.cpp
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,21 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+#pragma once
 
-namespace android::ui {
+#include <ftl/Flags.h>
 
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+#include <cstdint>
 
-} // namespace android::ui
+namespace android::scheduler {
+
+enum class Feature : std::uint8_t {
+    kPresentFences = 0b1,
+    kKernelIdleTimer = 0b10,
+    kContentDetection = 0b100,
+    kTracePredictedVsync = 0b1000,
+};
+
+using FeatureFlags = Flags<Feature>;
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
new file mode 100644
index 0000000..639b3e5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cmath>
+#include <ostream>
+#include <string>
+#include <type_traits>
+
+#include <android-base/stringprintf.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+// Frames per second, stored as floating-point frequency. Provides conversion from/to period in
+// nanoseconds, and relational operators with precision threshold.
+//
+//     const Fps fps = 60_Hz;
+//
+//     using namespace fps_approx_ops;
+//     assert(fps == Fps::fromPeriodNsecs(16'666'667));
+//
+class Fps {
+public:
+    constexpr Fps() = default;
+
+    static constexpr Fps fromValue(float frequency) {
+        return frequency > 0.f ? Fps(frequency, static_cast<nsecs_t>(1e9f / frequency)) : Fps();
+    }
+
+    static constexpr Fps fromPeriodNsecs(nsecs_t period) {
+        return period > 0 ? Fps(1e9f / period, period) : Fps();
+    }
+
+    constexpr bool isValid() const { return mFrequency > 0.f; }
+
+    constexpr float getValue() const { return mFrequency; }
+    int getIntValue() const { return static_cast<int>(std::round(mFrequency)); }
+
+    constexpr nsecs_t getPeriodNsecs() const { return mPeriod; }
+
+private:
+    constexpr Fps(float frequency, nsecs_t period) : mFrequency(frequency), mPeriod(period) {}
+
+    float mFrequency = 0.f;
+    nsecs_t mPeriod = 0;
+};
+
+static_assert(std::is_trivially_copyable_v<Fps>);
+
+constexpr Fps operator""_Hz(unsigned long long frequency) {
+    return Fps::fromValue(static_cast<float>(frequency));
+}
+
+constexpr Fps operator""_Hz(long double frequency) {
+    return Fps::fromValue(static_cast<float>(frequency));
+}
+
+inline bool isStrictlyLess(Fps lhs, Fps rhs) {
+    return lhs.getValue() < rhs.getValue();
+}
+
+// Does not satisfy equivalence relation.
+inline bool isApproxEqual(Fps lhs, Fps rhs) {
+    // TODO(b/185536303): Replace with ULP distance.
+    return std::abs(lhs.getValue() - rhs.getValue()) < 0.001f;
+}
+
+// Does not satisfy strict weak order.
+inline bool isApproxLess(Fps lhs, Fps rhs) {
+    return isStrictlyLess(lhs, rhs) && !isApproxEqual(lhs, rhs);
+}
+
+namespace fps_approx_ops {
+
+inline bool operator==(Fps lhs, Fps rhs) {
+    return isApproxEqual(lhs, rhs);
+}
+
+inline bool operator<(Fps lhs, Fps rhs) {
+    return isApproxLess(lhs, rhs);
+}
+
+inline bool operator!=(Fps lhs, Fps rhs) {
+    return !isApproxEqual(lhs, rhs);
+}
+
+inline bool operator>(Fps lhs, Fps rhs) {
+    return isApproxLess(rhs, lhs);
+}
+
+inline bool operator<=(Fps lhs, Fps rhs) {
+    return !isApproxLess(rhs, lhs);
+}
+
+inline bool operator>=(Fps lhs, Fps rhs) {
+    return !isApproxLess(lhs, rhs);
+}
+
+} // namespace fps_approx_ops
+
+struct FpsApproxEqual {
+    bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
+};
+
+inline std::string to_string(Fps fps) {
+    return base::StringPrintf("%.2f Hz", fps.getValue());
+}
+
+inline std::ostream& operator<<(std::ostream& stream, Fps fps) {
+    return stream << to_string(fps);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Seamlessness.h b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
similarity index 67%
rename from services/surfaceflinger/Scheduler/Seamlessness.h
rename to services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
index 3e42a4d..93bf726 100644
--- a/services/surfaceflinger/Scheduler/Seamlessness.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
@@ -16,11 +16,11 @@
 
 #pragma once
 
-#include <cstring>
 #include <ostream>
 
-namespace android {
-namespace scheduler {
+#include <ftl/enum.h>
+
+namespace android::scheduler {
 
 // The seamlessness requirement of a Layer.
 enum class Seamlessness {
@@ -31,24 +31,14 @@
     // Indicates no preference for seamlessness. For such layers the system will
     // prefer seamless switches, but also non-seamless switches to the group of the
     // default config are allowed.
-    Default
+    Default,
+
+    ftl_last = Default
 };
 
-inline std::string toString(Seamlessness seamlessness) {
-    switch (seamlessness) {
-        case Seamlessness::OnlySeamless:
-            return "OnlySeamless";
-        case Seamlessness::SeamedAndSeamless:
-            return "SeamedAndSeamless";
-        case Seamlessness::Default:
-            return "Default";
-    }
-}
-
 // Used by gtest
-inline std::ostream& operator<<(std::ostream& os, Seamlessness val) {
-    return os << toString(val);
+inline std::ostream& operator<<(std::ostream& os, Seamlessness s) {
+    return os << ftl::enum_string(s);
 }
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 615af02..63fbe78 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -26,6 +26,7 @@
 
 #include <android-base/properties.h>
 #include <android/configuration.h>
+#include <android/gui/IDisplayEventConnection.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
@@ -51,7 +52,6 @@
 #include <ftl/future.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
-#include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/LayerMetadata.h>
@@ -67,6 +67,7 @@
 #include <renderengine/RenderEngine.h>
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
+#include <ui/DataspaceUtils.h>
 #include <ui/DebugUtils.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayMode.h>
@@ -93,6 +94,7 @@
 #include <type_traits>
 #include <unordered_map>
 
+#include "BackgroundExecutor.h"
 #include "BufferLayer.h"
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
@@ -109,6 +111,7 @@
 #include "DisplayRenderArea.h"
 #include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
+#include "FlagManager.h"
 #include "FpsReporter.h"
 #include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
@@ -124,7 +127,6 @@
 #include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
-#include "Scheduler/MessageQueue.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
 #include "Scheduler/VsyncController.h"
@@ -138,6 +140,8 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+
 #define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
 
 #define ON_MAIN_THREAD(expr)                                       \
@@ -158,6 +162,8 @@
 #define NO_THREAD_SAFETY_ANALYSIS \
     _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
 
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
+
 namespace android {
 
 using namespace std::string_literals;
@@ -168,6 +174,8 @@
 
 using android::hardware::power::Boost;
 using base::StringAppendF;
+using gui::DisplayInfo;
+using gui::IDisplayEventConnection;
 using gui::IWindowInfosListener;
 using gui::WindowInfo;
 using ui::ColorMode;
@@ -261,18 +269,11 @@
     return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
 }
 
-class FrameRateFlexibilityToken : public BBinder {
-public:
-    FrameRateFlexibilityToken(std::function<void()> callback) : mCallback(callback) {}
-    virtual ~FrameRateFlexibilityToken() { mCallback(); }
-
-private:
-    std::function<void()> mCallback;
-};
 
 enum Permission {
     ACCESS_SURFACE_FLINGER = 0x1,
     ROTATE_SURFACE_FLINGER = 0x2,
+    INTERNAL_SYSTEM_WINDOW = 0x4,
 };
 
 struct IdleTimerConfig {
@@ -320,6 +321,7 @@
 const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
 const String16 sDump("android.permission.DUMP");
 const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
+const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
 
 const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
@@ -332,15 +334,12 @@
 uint32_t SurfaceFlinger::maxGraphicsHeight;
 bool SurfaceFlinger::hasWideColorDisplay;
 ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
-bool SurfaceFlinger::useColorManagement;
 bool SurfaceFlinger::useContextPriority;
 Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
-bool SurfaceFlinger::useFrameRateApi;
-bool SurfaceFlinger::enableSdrDimming;
-bool SurfaceFlinger::enableLatchUnsignaled;
+LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
@@ -364,13 +363,21 @@
             PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
 }
 
+bool callingThreadHasInternalSystemWindowAccess() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+        PermissionCache::checkPermission(sInternalSystemWindow, pid, uid);
+}
+
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
+        mPid(getpid()),
         mInterceptor(mFactory.createSurfaceInterceptor()),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(mFactory.createFrameTracer()),
-        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
-        mEventQueue(mFactory.createMessageQueue()),
+        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
         mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
@@ -396,11 +403,6 @@
     maxGraphicsHeight = std::max(max_graphics_height(0), 0);
 
     hasWideColorDisplay = has_wide_color_display(false);
-
-    // Android 12 and beyond, color management in display pipeline is turned on
-    // by default.
-    useColorManagement = use_color_management(true);
-
     mDefaultCompositionDataspace =
             static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
     mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace(
@@ -450,10 +452,7 @@
     property_get("ro.build.type", value, "user");
     mIsUserBuild = strcmp(value, "user") == 0;
 
-    property_get("debug.sf.showupdates", value, "0");
-    mDebugRegion = atoi(value);
-
-    ALOGI_IF(mDebugRegion, "showupdates enabled");
+    mDebugFlashDelay = base::GetUintProperty("debug.sf.showupdates"s, 0u);
 
     // DDMS debugging deprecated (b/120782499)
     property_get("debug.sf.ddms", value, "0");
@@ -501,28 +500,35 @@
         android::hardware::details::setTrebleTestingOverride(true);
     }
 
-    useFrameRateApi = use_frame_rate_api(true);
-
     mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
 
-    // Debug property overrides ro. property
-    enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
+    enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
 
-    enableLatchUnsignaled = base::GetBoolProperty("debug.sf.latch_unsignaled"s, false);
+    mTransactionTracingEnabled =
+            !mIsUserBuild && property_get_bool("debug.sf.enable_transaction_tracing", true);
+    if (mTransactionTracingEnabled) {
+        mTransactionTracing.enable();
+    }
+}
+
+LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
+    if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
+        return LatchUnsignaledConfig::Always;
+    } else if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
+        return LatchUnsignaledConfig::Auto;
+    } else {
+        return LatchUnsignaledConfig::Disabled;
+    }
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
 
-void SurfaceFlinger::onFirstRef() {
-    mEventQueue->init(this);
-}
-
 void SurfaceFlinger::binderDied(const wp<IBinder>&) {
     // the window manager died on us. prepare its eulogy.
     mBootFinished = false;
 
-    // Sever the link to inputflinger since its gone as well.
-    static_cast<void>(schedule([=] { mInputFlinger = nullptr; }));
+    // Sever the link to inputflinger since it's gone as well.
+    static_cast<void>(mScheduler->schedule([=] { mInputFlinger = nullptr; }));
 
     // restore initial conditions (default device unblank, etc)
     initializeDisplays();
@@ -532,16 +538,7 @@
 }
 
 void SurfaceFlinger::run() {
-    while (true) {
-        mEventQueue->waitMessage();
-    }
-}
-
-template <typename F, typename T>
-inline std::future<T> SurfaceFlinger::schedule(F&& f) {
-    auto [task, future] = makeTask(std::move(f));
-    mEventQueue->postMessage(std::move(task));
-    return std::move(future);
+    mScheduler->run();
 }
 
 sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
@@ -650,17 +647,20 @@
 }
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
-    const auto display = getDefaultDisplayDeviceLocked();
-    if (!display) {
-        return {};
-    }
-
     std::vector<PhysicalDisplayId> displayIds;
     displayIds.reserve(mPhysicalDisplayTokens.size());
-    displayIds.push_back(display->getPhysicalId());
+    const auto defaultDisplayId = [this]() REQUIRES(mStateLock) {
+        if (const auto display = getDefaultDisplayDeviceLocked()) {
+            return display->getPhysicalId();
+        }
+
+        // fallback to the internal display id if the active display is unknown
+        return getInternalDisplayIdLocked();
+    }();
+    displayIds.push_back(defaultDisplayId);
 
     for (const auto& [id, token] : mPhysicalDisplayTokens) {
-        if (id != display->getPhysicalId()) {
+        if (id != defaultDisplayId) {
             displayIds.push_back(id);
         }
     }
@@ -670,12 +670,7 @@
 
 status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const {
     Mutex::Autolock lock(mStateLock);
-    const auto display = getInternalDisplayIdLocked();
-    if (!display) {
-        return NAME_NOT_FOUND;
-    }
-
-    *id = *display;
+    *id = getInternalDisplayIdLocked();
     return NO_ERROR;
 }
 
@@ -721,6 +716,7 @@
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
 
+    mFlagManager = std::make_unique<android::FlagManager>();
     mFrameTracer->initialize();
     mFrameTimeline->onBootFinished();
 
@@ -742,7 +738,7 @@
 
     sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
 
-    static_cast<void>(schedule([=] {
+    static_cast<void>(mScheduler->schedule([=] {
         if (input == nullptr) {
             ALOGE("Failed to link to input service");
         } else {
@@ -750,7 +746,18 @@
         }
 
         readPersistentProperties();
+        std::optional<pid_t> renderEngineTid = getRenderEngine().getRenderEngineTid();
+        std::vector<int32_t> tidList;
+        tidList.emplace_back(gettid());
+        if (renderEngineTid.has_value()) {
+            tidList.emplace_back(*renderEngineTid);
+        }
         mPowerAdvisor.onBootFinished();
+        mPowerAdvisor.enablePowerHint(mFlagManager->use_adpf_cpu_hint());
+        if (mPowerAdvisor.usePowerHintSession()) {
+            mPowerAdvisor.startPowerHintSession(tidList);
+        }
+
         mBootStage = BootStage::FINISHED;
 
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
@@ -783,7 +790,7 @@
     if (std::this_thread::get_id() == mMainThreadId) {
         return genTextures();
     } else {
-        return schedule(genTextures).get();
+        return mScheduler->schedule(genTextures).get();
     }
 }
 
@@ -795,6 +802,25 @@
     ATRACE_INT("TexturePoolSize", mTexturePool.size());
 }
 
+static std::optional<renderengine::RenderEngine::RenderEngineType>
+chooseRenderEngineTypeViaSysProp() {
+    char prop[PROPERTY_VALUE_MAX];
+    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
+
+    if (strcmp(prop, "gles") == 0) {
+        return renderengine::RenderEngine::RenderEngineType::GLES;
+    } else if (strcmp(prop, "threaded") == 0) {
+        return renderengine::RenderEngine::RenderEngineType::THREADED;
+    } else if (strcmp(prop, "skiagl") == 0) {
+        return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+    } else if (strcmp(prop, "skiaglthreaded") == 0) {
+        return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+    } else {
+        ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop);
+        return {};
+    }
+}
+
 // Do not call property_set on main thread which will be blocked by init
 // Use StartPropertySetThread instead.
 void SurfaceFlinger::init() {
@@ -805,19 +831,21 @@
     // Get a RenderEngine for the given display / config (can't fail)
     // TODO(b/77156734): We need to stop casting and use HAL types when possible.
     // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
-    mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
-            renderengine::RenderEngineCreationArgs::Builder()
-                    .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
-                    .setImageCacheSize(maxFrameBufferAcquiredBuffers)
-                    .setUseColorManagerment(useColorManagement)
-                    .setEnableProtectedContext(enable_protected_contents(false))
-                    .setPrecacheToneMapperShaderOnly(false)
-                    .setSupportsBackgroundBlur(mSupportsBlur)
-                    .setContextPriority(
-                            useContextPriority
-                                    ? renderengine::RenderEngine::ContextPriority::REALTIME
-                                    : renderengine::RenderEngine::ContextPriority::MEDIUM)
-                    .build()));
+    auto builder = renderengine::RenderEngineCreationArgs::Builder()
+                           .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+                           .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+                           .setUseColorManagerment(useColorManagement)
+                           .setEnableProtectedContext(enable_protected_contents(false))
+                           .setPrecacheToneMapperShaderOnly(false)
+                           .setSupportsBackgroundBlur(mSupportsBlur)
+                           .setContextPriority(
+                                   useContextPriority
+                                           ? renderengine::RenderEngine::ContextPriority::REALTIME
+                                           : renderengine::RenderEngine::ContextPriority::MEDIUM);
+    if (auto type = chooseRenderEngineTypeViaSysProp()) {
+        builder.setRenderEngineType(type.value());
+    }
+    mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));
     mMaxRenderTargetSize =
             std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
 
@@ -838,10 +866,10 @@
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
+    LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");
     const auto displayId = display->getPhysicalId();
     LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
-                        "Internal display is disconnected.");
+                        "Primary display is disconnected.");
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -916,9 +944,8 @@
 }
 
 bool SurfaceFlinger::authenticateSurfaceTextureLocked(
-        const sp<IGraphicBufferProducer>& bufferProducer) const {
-    sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
-    return mGraphicBufferProducerList.count(surfaceTextureBinder.get()) > 0;
+        const sp<IGraphicBufferProducer>& /* bufferProducer */) const {
+    return false;
 }
 
 status_t SurfaceFlinger::getSupportedFrameTimestamps(
@@ -1010,6 +1037,11 @@
         return NAME_NOT_FOUND;
     }
 
+    const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+    if (!displayId) {
+        return INVALID_OPERATION;
+    }
+
     info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
 
     const auto& supportedModes = display->getSupportedModes();
@@ -1046,7 +1078,7 @@
         outMode.refreshRate = Fps::fromPeriodNsecs(period).getValue();
 
         const auto vsyncConfigSet =
-                mVsyncConfiguration->getConfigsForRefreshRate(Fps(outMode.refreshRate));
+                mVsyncConfiguration->getConfigsForRefreshRate(Fps::fromValue(outMode.refreshRate));
         outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
         outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
         outMode.group = mode->getGroup();
@@ -1069,18 +1101,18 @@
     }
 
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    const auto displayId = display->getPhysicalId();
-    info->supportedColorModes = getDisplayColorModes(displayId);
-
+    info->supportedColorModes = getDisplayColorModes(*display);
     info->hdrCapabilities = display->getHdrCapabilities();
+
     info->autoLowLatencyModeSupported =
-            getHwComposer().hasDisplayCapability(displayId,
-                                                 hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
+            getHwComposer().hasDisplayCapability(*displayId,
+                                                 DisplayCapability::AUTO_LOW_LATENCY_MODE);
     std::vector<hal::ContentType> types;
-    getHwComposer().getSupportedContentTypes(displayId, &types);
+    getHwComposer().getSupportedContentTypes(*displayId, &types);
     info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
         return type == hal::ContentType::GAME;
     });
+
     return NO_ERROR;
 }
 
@@ -1107,8 +1139,8 @@
     }
 
     if (display->setDesiredActiveMode(info)) {
-        // This will trigger HWC refresh without resetting the idle timer.
-        repaintEverythingForHWC();
+        scheduleComposite(FrameHint::kNone);
+
         // Start receiving vsync samples now, so that we can detect a period
         // switch.
         mScheduler->resyncToHardwareVsync(true, info.mode->getVsyncPeriod());
@@ -1121,14 +1153,14 @@
     }
 }
 
-status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) {
     ATRACE_CALL();
 
     if (!displayToken) {
         return BAD_VALUE;
     }
 
-    auto future = schedule([=]() -> status_t {
+    auto future = mScheduler->schedule([=]() -> status_t {
         const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
         if (!display) {
             ALOGE("Attempt to set allowed display modes for invalid display token %p",
@@ -1162,7 +1194,7 @@
     return future.get();
 }
 
-void SurfaceFlinger::setActiveModeInternal() {
+void SurfaceFlinger::updateInternalStateWithChangedMode() {
     ATRACE_CALL();
 
     const auto display = getDefaultDisplayDeviceLocked();
@@ -1198,7 +1230,7 @@
     mRefreshRateStats->setRefreshRate(refreshRate);
     updatePhaseConfiguration(refreshRate);
 
-    if (upcomingModeInfo.event != Scheduler::ModeEvent::None) {
+    if (upcomingModeInfo.event != DisplayModeEvent::None) {
         mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
     }
 }
@@ -1217,9 +1249,10 @@
     updatePhaseConfiguration(refreshRate);
 }
 
-void SurfaceFlinger::performSetActiveMode() {
+void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
     ATRACE_CALL();
-    ALOGV("%s", __FUNCTION__);
+
+    std::optional<PhysicalDisplayId> displayToUpdateImmediately;
 
     for (const auto& iter : mDisplays) {
         const auto& display = iter.second;
@@ -1254,8 +1287,7 @@
               to_string(display->getId()).c_str());
 
         if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
-            // display is not valid or we are already in the requested mode
-            // on both cases there is nothing left to do
+            // we are already in the requested mode, there is nothing left to do
             desiredActiveModeChangeDone(display);
             continue;
         }
@@ -1266,7 +1298,7 @@
         const auto displayModeAllowed =
                 display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId());
         if (!displayModeAllowed) {
-            desiredActiveModeChangeDone(display);
+            clearDesiredActiveModeState(display);
             continue;
         }
 
@@ -1286,13 +1318,31 @@
         }
         mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
 
-        // Scheduler will submit an empty frame to HWC if needed.
-        mSetActiveModePending = true;
+        if (outTimeline.refreshRequired) {
+            // Scheduler will submit an empty frame to HWC.
+            mSetActiveModePending = true;
+        } else {
+            // Updating the internal state should be done outside the loop,
+            // because it can recreate a DisplayDevice and modify mDisplays
+            // which will invalidate the iterator.
+            displayToUpdateImmediately = display->getPhysicalId();
+        }
+    }
+
+    if (displayToUpdateImmediately) {
+        updateInternalStateWithChangedMode();
+
+        const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
+        const auto desiredActiveMode = display->getDesiredActiveMode();
+        if (desiredActiveMode &&
+            display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+            desiredActiveModeChangeDone(display);
+        }
     }
 }
 
 void SurfaceFlinger::disableExpensiveRendering() {
-    schedule([=]() MAIN_THREAD {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD {
         ATRACE_CALL();
         if (mPowerAdvisor.isUsingExpensiveRendering()) {
             const auto& displays = ON_MAIN_THREAD(mDisplays);
@@ -1301,18 +1351,20 @@
                 mPowerAdvisor.setExpensiveRenderingExpected(display->getId(), kDisable);
             }
         }
-    }).wait();
+    });
+
+    future.wait();
 }
 
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
-    auto modes = getHwComposer().getColorModes(displayId);
-    bool isInternalDisplay = displayId == getInternalDisplayIdLocked();
+std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
+    auto modes = getHwComposer().getColorModes(display.getPhysicalId());
 
-    // If it's built-in display and the configuration claims it's not wide color capable,
+    // If the display is internal and the configuration claims it's not wide color capable,
     // filter out all wide color modes. The typical reason why this happens is that the
     // hardware is not good enough to support GPU composition of wide color, and thus the
     // OEMs choose to disable this capability.
-    if (isInternalDisplay && !hasWideColorDisplay) {
+    if (display.getConnectionType() == ui::DisplayConnectionType::Internal &&
+        !hasWideColorDisplay) {
         const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
         modes.erase(newEnd, modes.end());
     }
@@ -1336,54 +1388,63 @@
 }
 
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
-    schedule([=]() MAIN_THREAD {
-        const auto displayId = getPhysicalDisplayIdLocked(displayToken);
-        if (!displayId) {
-            ALOGE("Invalid display token %p", displayToken.get());
-            return;
-        }
-        const auto modes = getDisplayColorModes(*displayId);
-        bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
-        if (mode < ColorMode::NATIVE || !exists) {
-            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
-                  decodeColorMode(mode).c_str(), mode, displayToken.get());
-            return;
-        }
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
                   decodeColorMode(mode).c_str(), mode, displayToken.get());
-        } else if (display->isVirtual()) {
+            return NAME_NOT_FOUND;
+        }
+
+        if (display->isVirtual()) {
             ALOGW("Attempt to set active color mode %s (%d) for virtual display",
                   decodeColorMode(mode).c_str(), mode);
-        } else {
-            display->getCompositionDisplay()->setColorProfile(
-                    compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN,
-                                                            RenderIntent::COLORIMETRIC,
-                                                            Dataspace::UNKNOWN});
+            return INVALID_OPERATION;
         }
-    }).wait();
 
+        const auto modes = getDisplayColorModes(*display);
+        const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
+
+        if (mode < ColorMode::NATIVE || !exists) {
+            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
+                  decodeColorMode(mode).c_str(), mode, displayToken.get());
+            return BAD_VALUE;
+        }
+
+        display->getCompositionDisplay()->setColorProfile(
+                {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN});
+
+        return NO_ERROR;
+    });
+
+    // TODO(b/195698395): Propagate error.
+    future.wait();
     return NO_ERROR;
 }
 
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
-    static_cast<void>(schedule([=]() MAIN_THREAD {
+    const char* const whence = __func__;
+    static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
             getHwComposer().setAutoLowLatencyMode(*displayId, on);
         } else {
-            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
         }
     }));
 }
 
 void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
-    static_cast<void>(schedule([=]() MAIN_THREAD {
+    const char* const whence = __func__;
+    static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
             const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
             getHwComposer().setContentType(*displayId, type);
         } else {
-            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
         }
     }));
 }
@@ -1442,17 +1503,18 @@
 status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
                                                           bool enable, uint8_t componentMask,
                                                           uint64_t maxFrames) {
-    return schedule([=]() MAIN_THREAD -> status_t {
-               if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-                   return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
-                                                                           componentMask,
-                                                                           maxFrames);
-               } else {
-                   ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
-                   return NAME_NOT_FOUND;
-               }
-           })
-            .get();
+    const char* const whence = __func__;
+    auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
+                                                                    componentMask, maxFrames);
+        } else {
+            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+            return NAME_NOT_FOUND;
+        }
+    });
+
+    return future.get();
 }
 
 status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
@@ -1494,14 +1556,15 @@
 }
 
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
-    schedule([=] {
+    auto future = mScheduler->schedule([=] {
         Mutex::Autolock lock(mStateLock);
 
         if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
-            mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
+            mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
         }
-    }).wait();
+    });
 
+    future.wait();
     return NO_ERROR;
 }
 
@@ -1517,12 +1580,14 @@
 
 status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
     outLayers->clear();
-    schedule([=] {
+    auto future = mScheduler->schedule([=] {
         const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
         mDrawingState.traverseInZOrder([&](Layer* layer) {
             outLayers->push_back(layer->getLayerDebugInfo(display.get()));
         });
-    }).wait();
+    });
+
+    future.wait();
     return NO_ERROR;
 }
 
@@ -1606,28 +1671,56 @@
     if (!displayId) {
         return NAME_NOT_FOUND;
     }
-    *outSupport =
-            getHwComposer().hasDisplayCapability(*displayId, hal::DisplayCapability::BRIGHTNESS);
+    *outSupport = getHwComposer().hasDisplayCapability(*displayId, DisplayCapability::BRIGHTNESS);
     return NO_ERROR;
 }
 
+bool SurfaceFlinger::hasVisibleHdrLayer(const sp<DisplayDevice>& display) {
+    bool hasHdrLayers = false;
+    mDrawingState.traverse([&,
+                            compositionDisplay = display->getCompositionDisplay()](Layer* layer) {
+        hasHdrLayers |= (layer->isVisible() &&
+                         compositionDisplay->includesLayer(layer->getCompositionEngineLayerFE()) &&
+                         isHdrDataspace(layer->getDataSpace()));
+    });
+    return hasHdrLayers;
+}
+
 status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
                                               const gui::DisplayBrightness& brightness) {
     if (!displayToken) {
         return BAD_VALUE;
     }
 
-    return ftl::chain(schedule([=]() MAIN_THREAD {
+    const char* const whence = __func__;
+    return ftl::chain(mScheduler->schedule([=]() MAIN_THREAD {
                if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                   if (enableSdrDimming) {
+                   const bool supportsDisplayBrightnessCommand =
+                           getHwComposer().getComposer()->isSupported(
+                                   Hwc2::Composer::OptionalFeature::DisplayBrightnessCommand);
+                   // If we support applying display brightness as a command, then we also support
+                   // dimming SDR layers.
+                   if (supportsDisplayBrightnessCommand) {
                        display->getCompositionDisplay()
                                ->setDisplayBrightness(brightness.sdrWhitePointNits,
                                                       brightness.displayBrightnessNits);
+                       MAIN_THREAD_GUARD(display->stageBrightness(brightness.displayBrightness));
+                       if (hasVisibleHdrLayer(display)) {
+                           scheduleComposite(FrameHint::kNone);
+                       } else {
+                           scheduleCommit(FrameHint::kNone);
+                       }
+                       return ftl::yield<status_t>(OK);
+                   } else {
+                       return getHwComposer()
+                               .setDisplayBrightness(display->getPhysicalId(),
+                                                     brightness.displayBrightness,
+                                                     Hwc2::Composer::DisplayBrightnessOptions{
+                                                             .applyImmediately = true});
                    }
-                   return getHwComposer().setDisplayBrightness(display->getPhysicalId(),
-                                                               brightness.displayBrightness);
+
                } else {
-                   ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+                   ALOGE("%s: Invalid display token %p", whence, displayToken.get());
                    return ftl::yield<status_t>(NAME_NOT_FOUND);
                }
            }))
@@ -1683,12 +1776,29 @@
     Boost powerBoost = static_cast<Boost>(boostId);
 
     if (powerBoost == Boost::INTERACTION) {
-        mScheduler->notifyTouchEvent();
+        mScheduler->onTouchHint();
     }
 
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                                     bool* outSupport) const {
+    if (!displayToken || !outSupport) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        return NAME_NOT_FOUND;
+    }
+    *outSupport =
+            getHwComposer().hasDisplayCapability(*displayId, DisplayCapability::DISPLAY_DECORATION);
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1700,21 +1810,26 @@
     return mScheduler->createDisplayEventConnection(handle, eventRegistration);
 }
 
-void SurfaceFlinger::signalTransaction() {
-    mScheduler->resetIdleTimer();
+void SurfaceFlinger::scheduleCommit(FrameHint hint) {
+    if (hint == FrameHint::kActive) {
+        mScheduler->resetIdleTimer();
+    }
     mPowerAdvisor.notifyDisplayUpdateImminent();
-    mEventQueue->invalidate();
+    mScheduler->scheduleFrame();
 }
 
-void SurfaceFlinger::signalLayerUpdate() {
-    mScheduler->resetIdleTimer();
-    mPowerAdvisor.notifyDisplayUpdateImminent();
-    mEventQueue->invalidate();
+void SurfaceFlinger::scheduleComposite(FrameHint hint) {
+    mMustComposite = true;
+    scheduleCommit(hint);
 }
 
-void SurfaceFlinger::signalRefresh() {
-    mRefreshPending = true;
-    mEventQueue->refresh();
+void SurfaceFlinger::scheduleRepaint() {
+    mGeometryDirty = true;
+    scheduleComposite(FrameHint::kActive);
+}
+
+void SurfaceFlinger::scheduleSample() {
+    static_cast<void>(mScheduler->schedule([this] { sample(); }));
 }
 
 nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
@@ -1727,7 +1842,15 @@
 
 void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                         std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
-    ATRACE_CALL();
+    const std::string tracePeriod = [vsyncPeriod]() {
+        if (ATRACE_ENABLED() && vsyncPeriod) {
+            std::stringstream ss;
+            ss << "(" << *vsyncPeriod << ")";
+            return ss.str();
+        }
+        return std::string();
+    }();
+    ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
 
     Mutex::Autolock lock(mStateLock);
     const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
@@ -1760,28 +1883,10 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
-                                             Scheduler::ModeEvent event) {
-    const auto display = getDefaultDisplayDeviceLocked();
-    if (!display || mBootStage != BootStage::FINISHED) {
-        return;
-    }
-    ATRACE_CALL();
-
-    // Don't do any updating if the current fps is the same as the new one.
-    if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
-        ALOGV("Skipping mode %d as it is not part of allowed modes",
-              refreshRate.getModeId().value());
-        return;
-    }
-
-    setDesiredActiveMode({refreshRate.getMode(), event});
-}
-
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
                                           hal::Connection connection) {
-    ALOGI("%s(%" PRIu64 ", %s)", __func__, hwcDisplayId,
-          connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
+    const bool connected = connection == hal::Connection::CONNECTED;
+    ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId);
 
     // Only lock if we're not on the main thread. This function is normally
     // called on a hwbinder thread, but for the primary display it's called on
@@ -1812,14 +1917,14 @@
 
 void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) {
     Mutex::Autolock lock(mStateLock);
-    repaintEverythingForHWC();
+    scheduleComposite(FrameHint::kNone);
 }
 
 void SurfaceFlinger::setVsyncEnabled(bool enabled) {
     ATRACE_CALL();
 
     // On main thread to avoid race conditions with display power state.
-    static_cast<void>(schedule([=]() MAIN_THREAD {
+    static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
         mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
 
         if (const auto display = getDefaultDisplayDeviceLocked();
@@ -1867,40 +1972,37 @@
                                                           : stats.vsyncTime + stats.vsyncPeriod;
 }
 
-void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
-    switch (what) {
-        case MessageQueue::INVALIDATE: {
-            onMessageInvalidate(vsyncId, expectedVSyncTime);
-            break;
-        }
-        case MessageQueue::REFRESH: {
-            onMessageRefresh();
-            break;
-        }
+bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) {
+    MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
+    // we set this once at the beginning of commit to ensure consistency throughout the whole frame
+    mPowerHintSessionData.sessionEnabled = mPowerAdvisor.usePowerHintSession();
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerHintSessionData.commitStart = systemTime();
     }
-}
 
-void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
-    const nsecs_t frameStart = systemTime();
     // calculate the expected present time once and use the cached
     // value throughout this frame to make sure all layers are
     // seeing this same value.
-    if (expectedVSyncTime >= frameStart) {
-        mExpectedPresentTime = expectedVSyncTime;
+    if (expectedVsyncTime >= frameTime) {
+        mExpectedPresentTime = expectedVsyncTime;
     } else {
-        const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameStart);
+        const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameTime);
         mExpectedPresentTime = calculateExpectedPresentTime(stats);
     }
 
     const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
-    mScheduledPresentTime = expectedVSyncTime;
+    mScheduledPresentTime = expectedVsyncTime;
 
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerAdvisor.setTargetWorkDuration(mExpectedPresentTime -
+                                            mPowerHintSessionData.commitStart);
+    }
     const auto vsyncIn = [&] {
         if (!ATRACE_ENABLED()) return 0.f;
         return (mExpectedPresentTime - systemTime()) / 1e6f;
     }();
-    ATRACE_FORMAT("onMessageInvalidate %" PRId64 " vsyncIn %.2fms%s", vsyncId, vsyncIn,
-                  mExpectedPresentTime == expectedVSyncTime ? "" : " (adjusted)");
+    ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId, vsyncIn,
+                  mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
 
     // When Backpressure propagation is enabled we want to give a small grace period
     // for the present fence to fire instead of just giving up on this frame to handle cases
@@ -1947,11 +2049,11 @@
     }
 
     // If we are in the middle of a mode change and the fence hasn't
-    // fired yet just wait for the next invalidate
+    // fired yet just wait for the next commit.
     if (mSetActiveModePending) {
         if (framePending) {
-            mEventQueue->invalidate();
-            return;
+            mScheduler->scheduleFrame();
+            return false;
         }
 
         // We received the present fence from the HWC, so we assume it successfully updated
@@ -1959,50 +2061,63 @@
         mSetActiveModePending = false;
         {
             Mutex::Autolock lock(mStateLock);
-            setActiveModeInternal();
+            updateInternalStateWithChangedMode();
         }
     }
 
     if (framePending) {
         if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
-            signalLayerUpdate();
-            return;
+            scheduleCommit(FrameHint::kNone);
+            return false;
         }
     }
 
     if (mTracingEnabledChanged) {
-        mTracingEnabled = mTracing.isEnabled();
+        mLayerTracingEnabled = mLayerTracing.isEnabled();
         mTracingEnabledChanged = false;
     }
 
     if (mRefreshRateOverlaySpinner) {
-        if (Mutex::Autolock lock(mStateLock);
-            const auto display = getDefaultDisplayDeviceLocked()) {
-            if (display) {
-                display->onInvalidate();
-            } else {
-                ALOGW("%s: default display is null", __func__);
-            }
+        Mutex::Autolock lock(mStateLock);
+        if (const auto display = getDefaultDisplayDeviceLocked()) {
+            display->animateRefreshRateOverlay();
         }
     }
 
-    bool refreshNeeded;
+    // Composite if transactions were committed, or if requested by HWC.
+    bool mustComposite = mMustComposite.exchange(false);
     {
-        mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) ||
-                mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) ||
-                mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS);
-        const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
-        ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
+        mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod));
 
-        mFrameTimeline->setSfWakeUp(vsyncId, frameStart, Fps::fromPeriodNsecs(stats.vsyncPeriod));
-
-        refreshNeeded = handleMessageTransaction();
-        refreshNeeded |= handleMessageInvalidate();
-        if (tracePreComposition) {
-            if (mVisibleRegionsDirty) {
-                mTracing.notifyLocked("visibleRegionsDirty");
-            }
+        bool needsTraversal = false;
+        if (clearTransactionFlags(eTransactionFlushNeeded)) {
+            needsTraversal = flushTransactionQueues(vsyncId);
         }
+
+        const bool shouldCommit =
+                (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
+        if (shouldCommit) {
+            commitTransactions();
+        }
+
+        if (transactionFlushNeeded()) {
+            setTransactionFlags(eTransactionFlushNeeded);
+        }
+
+        mustComposite |= shouldCommit;
+        mustComposite |= latchBuffers();
+
+        // This has to be called after latchBuffers because we want to include the layers that have
+        // been latched in the commit callback
+        if (!needsTraversal) {
+            // Invoke empty transaction callbacks early.
+            mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
+        } else {
+            // Invoke OnCommit callbacks.
+            mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */);
+        }
+
+        updateLayerGeometry();
     }
 
     // Layers need to get updated (in the previous line) before we can use them for
@@ -2012,60 +2127,23 @@
     {
         Mutex::Autolock _l(mStateLock);
         mScheduler->chooseRefreshRateForContent();
+        setActiveModeInHwcIfNeeded();
     }
 
-    ON_MAIN_THREAD(performSetActiveMode());
-
     updateCursorAsync();
     updateInputFlinger();
 
-    refreshNeeded |= mRepaintEverything;
-    if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
-        // Signal a refresh if a transaction modified the window state,
-        // a new buffer was latched, or if HWC has requested a full
-        // repaint
-        if (mFrameStartTime <= 0) {
-            // We should only use the time of the first invalidate
-            // message that signals a refresh as the beginning of the
-            // frame. Otherwise the real frame time will be
-            // underestimated.
-            mFrameStartTime = frameStart;
-        }
+    MAIN_THREAD_GUARD(persistDisplayBrightness(mustComposite));
 
-        // Run the refresh immediately after invalidate as there is no point going thru the message
-        // queue again, and to ensure that we actually refresh the screen instead of handling
-        // other messages that were queued us already in the MessageQueue.
-        mRefreshPending = true;
-        onMessageRefresh();
-    }
-    notifyRegionSamplingThread();
+    return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
 }
 
-bool SurfaceFlinger::handleMessageTransaction() {
+void SurfaceFlinger::composite(nsecs_t frameTime) {
     ATRACE_CALL();
-
-    if (getTransactionFlags(eTransactionFlushNeeded)) {
-        flushTransactionQueues();
+    MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerHintSessionData.compositeStart = systemTime();
     }
-    uint32_t transactionFlags = peekTransactionFlags();
-    bool runHandleTransaction =
-            ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal;
-
-    if (runHandleTransaction) {
-        handleTransaction(eTransactionMask);
-    }
-
-    if (transactionFlushNeeded()) {
-        setTransactionFlags(eTransactionFlushNeeded);
-    }
-
-    return runHandleTransaction;
-}
-
-void SurfaceFlinger::onMessageRefresh() {
-    ATRACE_CALL();
-
-    mRefreshPending = false;
 
     compositionengine::CompositionRefreshArgs refreshArgs;
     const auto& displays = ON_MAIN_THREAD(mDisplays);
@@ -2083,7 +2161,6 @@
             refreshArgs.layersWithQueuedFrames.push_back(layerFE);
     }
 
-    refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
     refreshArgs.outputColorSetting = useColorManagement
             ? mDisplayColorSetting
             : compositionengine::OutputColorSetting::kUnmanaged;
@@ -2091,7 +2168,7 @@
     refreshArgs.forceOutputColorMode = mForceColorMode;
 
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
-    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
+    refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
     refreshArgs.blursAreExpensive = mBlursAreExpensive;
     refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
 
@@ -2100,31 +2177,34 @@
         mDrawingState.colorMatrixChanged = false;
     }
 
-    refreshArgs.devOptForceClientComposition = mDebugDisableHWC || mDebugRegion;
+    refreshArgs.devOptForceClientComposition = mDebugDisableHWC;
 
-    if (mDebugRegion != 0) {
-        refreshArgs.devOptFlashDirtyRegionsDelay =
-                std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
+    if (mDebugFlashDelay != 0) {
+        refreshArgs.devOptForceClientComposition = true;
+        refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
     }
 
-    const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+    const auto expectedPresentTime = mExpectedPresentTime.load();
+    const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(expectedPresentTime);
     const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
     refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
     refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
-    refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate();
-
-    mGeometryInvalid = false;
+    refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
+    refreshArgs.expectedPresentTime = expectedPresentTime;
 
     // Store the present time just before calling to the composition engine so we could notify
     // the scheduler.
     const auto presentTime = systemTime();
 
     mCompositionEngine->present(refreshArgs);
-    mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
-    // Reset the frame start time now that we've recorded this frame.
-    mFrameStartTime = 0;
 
-    mScheduler->onDisplayRefreshed(presentTime);
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerHintSessionData.presentEnd = systemTime();
+    }
+
+    mTimeStats->recordFrameDuration(frameTime, systemTime());
+
+    mScheduler->onPostComposition(presentTime);
 
     postFrame();
     postComposition();
@@ -2154,12 +2234,12 @@
     modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
 
     mLayersWithQueuedFrames.clear();
-    if (mTracingEnabled && mTracePostComposition) {
-        // This may block if SurfaceTracing is running in sync mode.
+    if (mLayerTracingEnabled) {
+        // This will block and should only be used for debugging.
         if (mVisibleRegionsDirty) {
-            mTracing.notify("visibleRegionsDirty");
-        } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) {
-            mTracing.notify("bufferLatched");
+            mLayerTracing.notify("visibleRegionsDirty");
+        } else if (mLayerTracing.flagIsSet(LayerTracing::TRACE_BUFFERS)) {
+            mLayerTracing.notify("bufferLatched");
         }
     }
 
@@ -2167,16 +2247,19 @@
     mVisibleRegionsDirty = false;
 
     if (mCompositionEngine->needsAnotherUpdate()) {
-        signalLayerUpdate();
+        scheduleCommit(FrameHint::kNone);
+    }
+
+    // calculate total render time for performance hinting if adpf cpu hint is enabled,
+    if (mPowerHintSessionData.sessionEnabled) {
+        const nsecs_t flingerDuration =
+                (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart);
+        mPowerAdvisor.sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd);
     }
 }
 
-bool SurfaceFlinger::handleMessageInvalidate() {
+void SurfaceFlinger::updateLayerGeometry() {
     ATRACE_CALL();
-    bool refreshNeeded = handlePageFlip();
-
-    // Send on commit callbacks
-    mTransactionCallbackInvoker.sendCallbacks();
 
     if (mVisibleRegionsDirty) {
         computeLayerBounds();
@@ -2188,7 +2271,6 @@
         invalidateLayerStack(layer, visibleReg);
     }
     mLayersPendingRefresh.clear();
-    return refreshNeeded;
 }
 
 void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
@@ -2297,13 +2379,9 @@
     }
 
     for (const auto& layer: mLayersWithQueuedFrames) {
-        const bool frameLatched =
-                layer->onPostComposition(display, glCompositionDoneFenceTime,
-                                         mPreviousPresentFences[0].fenceTime, compositorTiming);
+        layer->onPostComposition(display, glCompositionDoneFenceTime,
+                                 mPreviousPresentFences[0].fenceTime, compositorTiming);
         layer->releasePendingBuffer(/*dequeueReadyTime*/ now);
-        if (frameLatched) {
-            recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
-        }
     }
 
     std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
@@ -2336,13 +2414,8 @@
             int32_t maxArea = 0;
             mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
                 const auto layerFe = layer->getCompositionEngineLayerFE();
-                if (layer->isVisible() && compositionDisplay->belongsInOutput(layerFe)) {
-                    const Dataspace transfer =
-                        static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
-                    const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
-                                        transfer == Dataspace::TRANSFER_HLG);
-
-                    if (isHdr) {
+                if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
+                    if (isHdrDataspace(layer->getDataSpace())) {
                         const auto* outputLayer =
                             compositionDisplay->getOutputLayerForLayer(layerFe);
                         if (outputLayer) {
@@ -2366,7 +2439,8 @@
     mVisibleRegionsWereDirtyThisFrame = false;
 
     mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
-    mTransactionCallbackInvoker.sendCallbacks();
+    mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
+    mTransactionCallbackInvoker.clearCompletedTransactions();
 
     if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON &&
         mPreviousPresentFences[0].fenceTime->isValid()) {
@@ -2498,29 +2572,26 @@
     }
 }
 
-void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) {
+void SurfaceFlinger::commitTransactions() {
     ATRACE_CALL();
 
-    // here we keep a copy of the drawing state (that is the state that's
-    // going to be overwritten by handleTransactionLocked()) outside of
-    // mStateLock so that the side-effects of the State assignment
-    // don't happen with mStateLock held (which can cause deadlocks).
+    // Keep a copy of the drawing state (that is going to be overwritten
+    // by commitTransactionsLocked) outside of mStateLock so that the side
+    // effects of the State assignment don't happen with mStateLock held,
+    // which can cause deadlocks.
     State drawingState(mDrawingState);
 
-    Mutex::Autolock _l(mStateLock);
+    Mutex::Autolock lock(mStateLock);
     mDebugInTransaction = systemTime();
 
     // Here we're guaranteed that some transaction flags are set
-    // so we can call handleTransactionLocked() unconditionally.
-    // We call getTransactionFlags(), which will also clear the flags,
-    // with mStateLock held to guarantee that mCurrentState won't change
-    // until the transaction is committed.
+    // so we can call commitTransactionsLocked unconditionally.
+    // We clear the flags with mStateLock held to guarantee that
+    // mCurrentState won't change until the transaction is committed.
     modulateVsync(&VsyncModulator::onTransactionCommit);
-    transactionFlags = getTransactionFlags(eTransactionMask);
-    handleTransactionLocked(transactionFlags);
+    commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
 
     mDebugInTransaction = 0;
-    // here the transaction has been committed
 }
 
 void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
@@ -2757,6 +2828,7 @@
     display->setProjection(state.orientation, state.layerStackSpaceRect,
                            state.orientedDisplaySpaceRect);
     display->setDisplayName(state.displayName);
+    display->setFlags(state.flags);
 
     return display;
 }
@@ -2787,14 +2859,12 @@
     compositionengine::DisplayCreationArgsBuilder builder;
     if (const auto& physical = state.physical) {
         builder.setId(physical->id);
-        builder.setConnectionType(physical->type);
     } else {
         builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
     }
 
     builder.setPixels(resolution);
     builder.setIsSecure(state.isSecure);
-    builder.setLayerStackId(state.layerStack);
     builder.setPowerAdvisor(&mPowerAdvisor);
     builder.setName(state.displayName);
     auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -2854,7 +2924,7 @@
     mDisplays.erase(displayToken);
 
     if (display && display->isVirtual()) {
-        static_cast<void>(schedule([display = std::move(display)] {
+        static_cast<void>(mScheduler->schedule([display = std::move(display)] {
             // Destroy the display without holding the mStateLock.
             // This is a temporary solution until we can manage transaction queues without
             // holding the mStateLock.
@@ -2900,7 +2970,7 @@
             setPowerModeInternal(display, hal::PowerMode::ON);
 
             // TODO(b/175678251) Call a listener instead.
-            if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+            if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
                 updateInternalDisplayVsyncLocked(display);
             }
         }
@@ -2980,14 +3050,13 @@
     mDrawingState.displays = mCurrentState.displays;
 }
 
-void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
-    // Commit display transactions
+void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) {
+    // Commit display transactions.
     const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
     if (displayTransactionNeeded) {
         processDisplayChangesLocked();
         processDisplayHotplugEventsLocked();
     }
-    mForceTraversal = false;
     mForceTransactionDisplayChange = displayTransactionNeeded;
 
     if (mSomeChildrenChanged) {
@@ -2995,18 +3064,9 @@
         mSomeChildrenChanged = false;
     }
 
-    // Update transform hint
+    // Update transform hint.
     if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
-        // The transform hint might have changed for some layers
-        // (either because a display has changed, or because a layer
-        // as changed).
-        //
-        // Walk through all the layers in currentLayers,
-        // and update their transform hint.
-        //
-        // If a layer is visible only on a single display, then that
-        // display is used to calculate the hint, otherwise we use the
-        // default display.
+        // Layers and/or displays have changed, so update the transform hint for each layer.
         //
         // NOTE: we do this here, rather than when presenting the display so that
         // the hint is set before we acquire a buffer from the surface texture.
@@ -3017,30 +3077,29 @@
         // (soon to become the drawing state list).
         //
         sp<const DisplayDevice> hintDisplay;
-        uint32_t currentlayerStack = 0;
-        bool first = true;
+        ui::LayerStack layerStack;
+
         mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
-            uint32_t layerStack = layer->getLayerStack();
-            if (first || currentlayerStack != layerStack) {
-                currentlayerStack = layerStack;
-                // figure out if this layerstack is mirrored
-                // (more than one display) if so, pick the default display,
-                // if not, pick the only display it's on.
+            if (const auto filter = layer->getOutputFilter(); layerStack != filter.layerStack) {
+                layerStack = filter.layerStack;
                 hintDisplay = nullptr;
+
+                // Find the display that includes the layer.
                 for (const auto& [token, display] : mDisplays) {
-                    if (display->getCompositionDisplay()
-                                ->belongsInOutput(layer->getLayerStack(),
-                                                  layer->getPrimaryDisplayOnly())) {
-                        if (hintDisplay) {
-                            hintDisplay = nullptr;
-                            break;
-                        } else {
-                            hintDisplay = display;
-                        }
+                    if (!display->getCompositionDisplay()->includesLayer(filter)) {
+                        continue;
                     }
+
+                    // Pick the primary display if another display mirrors the layer.
+                    if (hintDisplay) {
+                        hintDisplay = nullptr;
+                        break;
+                    }
+
+                    hintDisplay = display;
                 }
             }
 
@@ -3054,20 +3113,10 @@
                 hintDisplay = getDefaultDisplayDeviceLocked();
             }
 
-            // could be null if there is no display available at all to get
-            // the transform hint from.
-            if (hintDisplay) {
-                layer->updateTransformHint(hintDisplay->getTransformHint());
-            }
-
-            first = false;
+            layer->updateTransformHint(hintDisplay->getTransformHint());
         });
     }
 
-    /*
-     * Perform our own transaction if needed
-     */
-
     if (mLayersAdded) {
         mLayersAdded = false;
         // Layers have been added.
@@ -3089,7 +3138,9 @@
         });
     }
 
-    commitTransaction();
+    doCommitTransactions();
+    signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
+    mAnimTransactionPending = false;
 }
 
 void SurfaceFlinger::updateInputFlinger() {
@@ -3098,41 +3149,125 @@
         return;
     }
 
+    std::vector<WindowInfo> windowInfos;
+    std::vector<DisplayInfo> displayInfos;
+    bool updateWindowInfo = false;
     if (mVisibleRegionsDirty || mInputInfoChanged) {
         mInputInfoChanged = false;
-        notifyWindowInfos();
-    } else if (mInputWindowCommands.syncInputWindows) {
-        // If the caller requested to sync input windows, but there are no
-        // changes to input windows, notify immediately.
-        windowInfosReported();
+        updateWindowInfo = true;
+        buildWindowInfos(windowInfos, displayInfos);
     }
+    if (!updateWindowInfo && mInputWindowCommands.empty()) {
+        return;
+    }
+    BackgroundExecutor::getInstance().execute([updateWindowInfo,
+                                               windowInfos = std::move(windowInfos),
+                                               displayInfos = std::move(displayInfos),
+                                               inputWindowCommands =
+                                                       std::move(mInputWindowCommands),
+                                               inputFlinger = mInputFlinger, this]() {
+        ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
+        if (updateWindowInfo) {
+            mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,
+                                                            inputWindowCommands.syncInputWindows);
+        } else if (inputWindowCommands.syncInputWindows) {
+            // If the caller requested to sync input windows, but there are no
+            // changes to input windows, notify immediately.
+            windowInfosReported();
+        }
+        for (const auto& focusRequest : inputWindowCommands.focusRequests) {
+            inputFlinger->setFocusedWindow(focusRequest);
+        }
+    });
 
-    for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
-        mInputFlinger->setFocusedWindow(focusRequest);
-    }
     mInputWindowCommands.clear();
 }
 
-bool enablePerWindowInputRotation() {
-    static bool value =
-            android::base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
-    return value;
+void SurfaceFlinger::persistDisplayBrightness(bool needsComposite) {
+    const bool supportsDisplayBrightnessCommand = getHwComposer().getComposer()->isSupported(
+            Hwc2::Composer::OptionalFeature::DisplayBrightnessCommand);
+    if (!supportsDisplayBrightnessCommand) {
+        return;
+    }
+
+    const auto& displays = ON_MAIN_THREAD(mDisplays);
+
+    for (const auto& [_, display] : displays) {
+        if (const auto brightness = display->getStagedBrightness(); brightness) {
+            if (!needsComposite) {
+                const status_t error =
+                        getHwComposer()
+                                .setDisplayBrightness(display->getPhysicalId(), *brightness,
+                                                      Hwc2::Composer::DisplayBrightnessOptions{
+                                                              .applyImmediately = true})
+                                .get();
+
+                ALOGE_IF(error != NO_ERROR,
+                         "Error setting display brightness for display %s: %d (%s)",
+                         display->getDebugName().c_str(), error, strerror(error));
+            }
+            display->persistBrightness(needsComposite);
+        }
+    }
 }
 
-void SurfaceFlinger::notifyWindowInfos() {
-    std::vector<WindowInfo> windowInfos;
+void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
+                                      std::vector<DisplayInfo>& outDisplayInfos) {
+    struct Details {
+        Details(bool receivesInput, bool isSecure, const ui::Transform& transform,
+                const DisplayInfo& info)
+              : receivesInput(receivesInput),
+                isSecure(isSecure),
+                transform(std::move(transform)),
+                info(std::move(info)) {}
+        bool receivesInput;
+        bool isSecure;
+        ui::Transform transform;
+        DisplayInfo info;
+    };
+    std::unordered_map<uint32_t /*layerStackId*/, Details> inputDisplayDetails;
+    for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+        const uint32_t layerStackId = display->getLayerStack().id;
+        const auto& [info, transform] = display->getInputInfo();
+        const auto& [it, emplaced] =
+                inputDisplayDetails.try_emplace(layerStackId, display->receivesInput(),
+                                                display->isSecure(), transform, info);
+        if (emplaced) {
+            continue;
+        }
+
+        // There is more than one display for the layerStack. In this case, the display that is
+        // configured to receive input takes precedence.
+        auto& details = it->second;
+        if (!display->receivesInput()) {
+            continue;
+        }
+        ALOGE_IF(details.receivesInput,
+                 "Multiple displays claim to accept input for the same layer stack: %u",
+                 layerStackId);
+        details.receivesInput = display->receivesInput();
+        details.isSecure = display->isSecure();
+        details.transform = std::move(transform);
+        details.info = std::move(info);
+    }
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
-        sp<DisplayDevice> display = enablePerWindowInputRotation()
-                ? ON_MAIN_THREAD(getDisplayWithInputByLayer(layer))
-                : nullptr;
-        // When calculating the screen bounds we ignore the transparent region since it may
-        // result in an unwanted offset.
-        windowInfos.push_back(layer->fillInputInfo(display));
+
+        const uint32_t layerStackId = layer->getLayerStack().id;
+        const auto it = inputDisplayDetails.find(layerStackId);
+        if (it == inputDisplayDetails.end()) {
+            // Do not create WindowInfos for windows on displays that cannot receive input.
+            return;
+        }
+
+        const auto& details = it->second;
+        outWindowInfos.push_back(layer->fillInputInfo(details.transform, details.isSecure));
     });
-    mWindowInfosListenerInvoker->windowInfosChanged(windowInfos,
-                                                    mInputWindowCommands.syncInputWindows);
+
+    for (const auto& [_, details] : inputDisplayDetails) {
+        outDisplayInfos.push_back(std::move(details.info));
+    }
 }
 
 void SurfaceFlinger::updateCursorAsync() {
@@ -3146,13 +3281,27 @@
     mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
-void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, Scheduler::ModeEvent event) {
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, DisplayModeEvent event) {
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
     // Scheduler::chooseRefreshRateForContent
 
     ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-    changeRefreshRateLocked(refreshRate, event);
+
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (!display || mBootStage != BootStage::FINISHED) {
+        return;
+    }
+    ATRACE_CALL();
+
+    // Don't do any updating if the current fps is the same as the new one.
+    if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
+        ALOGV("Skipping mode %d as it is not part of allowed modes",
+              refreshRate.getModeId().value());
+        return;
+    }
+
+    setDesiredActiveMode({refreshRate.getMode(), event});
 }
 
 void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3180,8 +3329,35 @@
     mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
     mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
 
-    // start the EventThread
-    mScheduler = getFactory().createScheduler(display->holdRefreshRateConfigs(), *this);
+    using Feature = scheduler::Feature;
+    scheduler::FeatureFlags features;
+
+    if (sysprop::use_content_detection_for_refresh_rate(false)) {
+        features |= Feature::kContentDetection;
+    }
+    if (base::GetBoolProperty("debug.sf.show_predicted_vsync"s, false)) {
+        features |= Feature::kTracePredictedVsync;
+    }
+    if (!base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false) &&
+        !getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+        features |= Feature::kPresentFences;
+    }
+
+    mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
+                                                        static_cast<ISchedulerCallback&>(*this),
+                                                        features);
+    {
+        auto configs = display->holdRefreshRateConfigs();
+        if (configs->supportsKernelIdleTimer()) {
+            features |= Feature::kKernelIdleTimer;
+        }
+
+        mScheduler->createVsyncSchedule(features);
+        mScheduler->setRefreshRateConfigs(std::move(configs));
+    }
+    setVsyncEnabled(false);
+    mScheduler->startTimers();
+
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
     const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
     mAppConnectionHandle =
@@ -3197,8 +3373,8 @@
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
-    mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
-                           configs.late.sfWorkDuration);
+    mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+                          configs.late.sfWorkDuration);
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
@@ -3211,11 +3387,6 @@
     // classes from EventThread, and there should be no run-time binder cost
     // anyway since there are no connected apps at this point.
     mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode());
-    static auto ignorePresentFences =
-            base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
-    mScheduler->setIgnorePresentFences(
-            ignorePresentFences ||
-            getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
 }
 
 void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -3232,22 +3403,15 @@
     mScheduler->setDuration(mSfConnectionHandle,
                             /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
                             /*readyDuration=*/config.sfWorkDuration);
-    mEventQueue->setDuration(config.sfWorkDuration);
+    mScheduler->setDuration(config.sfWorkDuration);
 }
 
-void SurfaceFlinger::commitTransaction() {
+void SurfaceFlinger::doCommitTransactions() {
     ATRACE_CALL();
-    commitTransactionLocked();
-    signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
-    mAnimTransactionPending = false;
-}
 
-void SurfaceFlinger::commitTransactionLocked() {
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (const auto& l : mLayersPendingRemoval) {
-            recordBufferingStats(l->getName(), l->getOccupancyHistory(true));
-
             // Ensure any buffers set to display on any children are released.
             if (l->isRemovedFromCurrentState()) {
                 l->latchAndReleaseBuffer();
@@ -3286,11 +3450,10 @@
 void SurfaceFlinger::commitOffscreenLayers() {
     for (Layer* offscreenLayer : mOffscreenLayers) {
         offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
-            uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-            if (!trFlags) return;
-
-            layer->doTransaction(0);
-            layer->commitChildList();
+            if (layer->clearTransactionFlags(eTransactionNeeded)) {
+                layer->doTransaction(0);
+                layer->commitChildList();
+            }
         });
     }
 }
@@ -3298,17 +3461,16 @@
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
     for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) {
         auto display = displayDevice->getCompositionDisplay();
-        if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+        if (display->includesLayer(layer->getOutputFilter())) {
             display->editState().dirtyRegion.orSelf(dirty);
         }
     }
 }
 
-bool SurfaceFlinger::handlePageFlip() {
+bool SurfaceFlinger::latchBuffers() {
     ATRACE_CALL();
-    ALOGV("handlePageFlip");
 
-    nsecs_t latchTime = systemTime();
+    const nsecs_t latchTime = systemTime();
 
     bool visibleRegions = false;
     bool frameQueued = false;
@@ -3326,14 +3488,14 @@
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
     mDrawingState.traverse([&](Layer* layer) {
-         uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-         if (trFlags || mForceTransactionDisplayChange) {
-             const uint32_t flags = layer->doTransaction(0);
-             if (flags & Layer::eVisibleRegion)
-                 mVisibleRegionsDirty = true;
-         }
+        if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
+            const uint32_t flags = layer->doTransaction(0);
+            if (flags & Layer::eVisibleRegion) {
+                mVisibleRegionsDirty = true;
+            }
+        }
 
-         if (layer->hasReadyFrame()) {
+        if (layer->hasReadyFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.emplace(layer);
@@ -3341,7 +3503,7 @@
                 ATRACE_NAME("!layer->shouldPresentNow()");
                 layer->useEmptyDamage();
             }
-         } else {
+        } else {
             layer->useEmptyDamage();
         }
     });
@@ -3377,7 +3539,7 @@
     // queued frame that shouldn't be displayed during this vsync period, wake
     // up during the next vsync period to check again.
     if (frameQueued && (mLayersWithQueuedFrames.empty() || !newDataLatched)) {
-        signalLayerUpdate();
+        scheduleCommit(FrameHint::kNone);
     }
 
     // enter boot animation on first buffer latch
@@ -3394,26 +3556,16 @@
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
 
-void SurfaceFlinger::invalidateHwcGeometry() {
-    mGeometryInvalid = true;
-}
-
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
-                                        const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
-                                        const sp<IBinder>& parentHandle,
-                                        const sp<Layer>& parentLayer, bool addToRoot,
-                                        uint32_t* outTransformHint) {
+                                        const sp<Layer>& lbc, const wp<Layer>& parent,
+                                        bool addToRoot, uint32_t* outTransformHint) {
     if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
         ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
               ISurfaceComposer::MAX_LAYERS);
         return NO_MEMORY;
     }
 
-    wp<IBinder> initialProducer;
-    if (gbc != nullptr) {
-        initialProducer = IInterface::asBinder(gbc);
-    }
-    setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer, addToRoot);
+    setLayerCreatedState(handle, lbc, parent, addToRoot);
 
     // Create a transaction includes the initial parent and producer.
     Vector<ComposerState> states;
@@ -3429,68 +3581,66 @@
         *outTransformHint = mActiveDisplayTransformHint;
     }
     // attach this layer to the client
-    client->attachLayer(handle, lbc);
+    if (client != nullptr) {
+        client->attachLayer(handle, lbc);
+    }
 
+    int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
     return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr,
                                InputWindowCommands{}, -1 /* desiredPresentTime */,
                                true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {},
-                               0 /* Undefined transactionId */);
+                               transactionId);
 }
 
-void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) {
-    static_cast<void>(schedule([=] {
-        Mutex::Autolock lock(mStateLock);
-        mGraphicBufferProducerList.erase(binder);
-    }));
-}
-
-uint32_t SurfaceFlinger::peekTransactionFlags() {
+uint32_t SurfaceFlinger::getTransactionFlags() const {
     return mTransactionFlags;
 }
 
-uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) {
-    return mTransactionFlags.fetch_and(~flags) & flags;
+uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) {
+    return mTransactionFlags.fetch_and(~mask) & mask;
 }
 
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
-    return setTransactionFlags(flags, TransactionSchedule::Late);
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask) {
+    return setTransactionFlags(mask, TransactionSchedule::Late);
 }
 
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule,
-                                             const sp<IBinder>& token) {
-    uint32_t old = mTransactionFlags.fetch_or(flags);
-    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, token);
-    if ((old & flags) == 0) signalTransaction();
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
+                                             const sp<IBinder>& applyToken) {
+    const uint32_t old = mTransactionFlags.fetch_or(mask);
+    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
+    if ((old & mask) == 0) scheduleCommit(FrameHint::kActive);
     return old;
 }
 
-void SurfaceFlinger::setTraversalNeeded() {
-    mForceTraversal = true;
-}
-
-void SurfaceFlinger::flushTransactionQueues() {
+bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
     // states) around outside the scope of the lock
-    std::vector<const TransactionState> transactions;
+    std::vector<TransactionState> transactions;
     // Layer handles that have transactions with buffers that are ready to be applied.
     std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
     {
         Mutex::Autolock _l(mStateLock);
         {
             Mutex::Autolock _l(mQueueLock);
+            // allowLatchUnsignaled acts as a filter condition when latch unsignaled is either auto
+            // or always. auto: in this case we let buffer latch unsignaled if we have only one
+            // applyToken and if only first transaction is latch unsignaled. If more than one
+            // applyToken we don't latch unsignaled.
+            bool allowLatchUnsignaled = allowedLatchUnsignaled();
+            bool isFirstUnsignaledTransactionApplied = false;
             // Collect transactions from pending transaction queue.
             auto it = mPendingTransactionQueues.begin();
             while (it != mPendingTransactionQueues.end()) {
                 auto& [applyToken, transactionQueue] = *it;
-
                 while (!transactionQueue.empty()) {
                     auto& transaction = transactionQueue.front();
                     if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                        transaction.isAutoTimestamp,
                                                        transaction.desiredPresentTime,
                                                        transaction.originUid, transaction.states,
-                                                       bufferLayersReadyToPresent)) {
+                                                       bufferLayersReadyToPresent,
+                                                       allowLatchUnsignaled)) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
@@ -3499,6 +3649,14 @@
                     });
                     transactions.emplace_back(std::move(transaction));
                     transactionQueue.pop();
+                    if (allowLatchUnsignaled &&
+                        enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+                        // if allowLatchUnsignaled && we are in LatchUnsignaledConfig::Auto
+                        // then we should have only one applyToken for processing.
+                        // so we can stop further transactions on this applyToken.
+                        isFirstUnsignaledTransactionApplied = true;
+                        break;
+                    }
                 }
 
                 if (transactionQueue.empty()) {
@@ -3510,48 +3668,123 @@
             }
 
             // Collect transactions from current transaction queue or queue to pending transactions.
-            // Case 1: push to pending when transactionIsReadyToBeApplied is false.
+            // Case 1: push to pending when transactionIsReadyToBeApplied is false
+            // or the first transaction was unsignaled.
             // Case 2: push to pending when there exist a pending queue.
-            // Case 3: others are ready to apply.
+            // Case 3: others are the transactions that are ready to apply.
             while (!mTransactionQueue.empty()) {
                 auto& transaction = mTransactionQueue.front();
                 bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
-                if (pendingTransactions ||
+                if (isFirstUnsignaledTransactionApplied || pendingTransactions ||
                     !transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                    transaction.isAutoTimestamp,
                                                    transaction.desiredPresentTime,
                                                    transaction.originUid, transaction.states,
-                                                   bufferLayersReadyToPresent)) {
+                                                   bufferLayersReadyToPresent,
+                                                   allowLatchUnsignaled)) {
                     mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
                     transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
                         bufferLayersReadyToPresent.insert(state.surface);
                     });
                     transactions.emplace_back(std::move(transaction));
+                    if (allowLatchUnsignaled &&
+                        enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+                        isFirstUnsignaledTransactionApplied = true;
+                    }
                 }
-                mTransactionQueue.pop();
+                mTransactionQueue.pop_front();
                 ATRACE_INT("TransactionQueue", mTransactionQueue.size());
             }
-        }
 
-        // Now apply all transactions.
-        for (const auto& transaction : transactions) {
-            applyTransactionState(transaction.frameTimelineInfo, transaction.states,
-                                  transaction.displays, transaction.flags,
-                                  transaction.inputWindowCommands, transaction.desiredPresentTime,
-                                  transaction.isAutoTimestamp, transaction.buffer,
-                                  transaction.postTime, transaction.permissions,
-                                  transaction.hasListenerCallbacks, transaction.listenerCallbacks,
-                                  transaction.originPid, transaction.originUid, transaction.id);
-            if (transaction.transactionCommittedSignal) {
-                mTransactionCommittedSignals.emplace_back(
-                        std::move(transaction.transactionCommittedSignal));
-            }
+            return applyTransactions(transactions, vsyncId);
         }
     }
 }
 
+bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
+                                       int64_t vsyncId) {
+    bool needsTraversal = false;
+    // Now apply all transactions.
+    for (const auto& transaction : transactions) {
+        needsTraversal |=
+                applyTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                      transaction.displays, transaction.flags,
+                                      transaction.inputWindowCommands,
+                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                      transaction.buffer, transaction.postTime,
+                                      transaction.permissions, transaction.hasListenerCallbacks,
+                                      transaction.listenerCallbacks, transaction.originPid,
+                                      transaction.originUid, transaction.id);
+        if (transaction.transactionCommittedSignal) {
+            mTransactionCommittedSignals.emplace_back(
+                    std::move(transaction.transactionCommittedSignal));
+        }
+    }
+
+    if (mTransactionTracingEnabled) {
+        mTransactionTracing.addCommittedTransactions(transactions, vsyncId);
+    }
+    return needsTraversal;
+}
+
+bool SurfaceFlinger::allowedLatchUnsignaled() {
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
+        return false;
+    }
+    // Always mode matches the current latch unsignaled behavior.
+    // This behavior is currently used by the partners and we would like
+    // to keep it until we are completely migrated to Auto mode successfully
+    // and we we have our fallback based implementation in place.
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
+        return true;
+    }
+
+    //  if enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto
+    //  we don't latch unsignaled if more than one applyToken, as it can backpressure
+    //  the other transactions.
+    if (mPendingTransactionQueues.size() > 1) {
+        return false;
+    }
+    std::optional<sp<IBinder>> applyToken = std::nullopt;
+    bool isPendingTransactionQueuesItem = false;
+    if (!mPendingTransactionQueues.empty()) {
+        applyToken = mPendingTransactionQueues.begin()->first;
+        isPendingTransactionQueuesItem = true;
+    }
+
+    for (const auto& item : mTransactionQueue) {
+        if (!applyToken.has_value()) {
+            applyToken = item.applyToken;
+        } else if (applyToken.has_value() && applyToken != item.applyToken) {
+            return false;
+        }
+    }
+
+    if (isPendingTransactionQueuesItem) {
+        return checkTransactionCanLatchUnsignaled(
+                mPendingTransactionQueues.begin()->second.front());
+    } else if (applyToken.has_value()) {
+        return checkTransactionCanLatchUnsignaled((mTransactionQueue.front()));
+    }
+    return false;
+}
+
+bool SurfaceFlinger::checkTransactionCanLatchUnsignaled(const TransactionState& transaction) {
+    if (transaction.states.size() == 1) {
+        const auto& state = transaction.states.begin()->state;
+        if ((state.flags & ~layer_state_t::eBufferChanged) == 0 &&
+            state.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) &&
+            state.bufferData.acquireFence &&
+            state.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+            ATRACE_NAME("transactionCanLatchUnsignaled");
+            return true;
+        }
+    }
+    return false;
+}
+
 bool SurfaceFlinger::transactionFlushNeeded() {
     Mutex::Autolock _l(mQueueLock);
     return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
@@ -3583,8 +3816,9 @@
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
         const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
-                bufferLayersReadyToPresent) const {
-    ATRACE_CALL();
+                bufferLayersReadyToPresent,
+        bool allowLatchUnsignaled) const {
+    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
@@ -3608,9 +3842,10 @@
 
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
-        const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
-        if (acquireFenceChanged && s.acquireFence && !enableLatchUnsignaled &&
-            s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+        const bool acquireFenceChanged =
+                s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged);
+        if (acquireFenceChanged && s.bufferData.acquireFence && !allowLatchUnsignaled &&
+            s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
             ATRACE_NAME("fence unsignaled");
             return false;
         }
@@ -3670,7 +3905,7 @@
                          : CountDownLatch::eSyncTransaction));
     }
 
-    mTransactionQueue.emplace(state);
+    mTransactionQueue.emplace_back(state);
     ATRACE_INT("TransactionQueue", mTransactionQueue.size());
 
     const auto schedule = [](uint32_t flags) {
@@ -3720,6 +3955,10 @@
         permissions |= Permission::ROTATE_SURFACE_FLINGER;
     }
 
+    if (callingThreadHasInternalSystemWindowAccess()) {
+        permissions |= Permission::INTERNAL_SYSTEM_WINDOW;
+    }
+
     if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
         (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
         ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
@@ -3744,6 +3983,10 @@
     state.traverseStatesWithBuffers([&](const layer_state_t& state) {
         mBufferCountTracker.increment(state.surface->localBinder());
     });
+
+    if (mTransactionTracingEnabled) {
+        mTransactionTracing.addQueuedTransaction(state);
+    }
     queueTransaction(state);
 
     // Check the pending state to make sure the transaction is synchronous.
@@ -3754,7 +3997,7 @@
     return NO_ERROR;
 }
 
-void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                            const Vector<ComposerState>& states,
                                            const Vector<DisplayState>& displays, uint32_t flags,
                                            const InputWindowCommands& inputWindowCommands,
@@ -3773,33 +4016,23 @@
     // that listeners with SurfaceControls will start registration during setClientStateLocked
     // below.
     for (const auto& listener : listenerCallbacks) {
-        mTransactionCallbackInvoker.startRegistration(listener);
-        mTransactionCallbackInvoker.endRegistration(listener);
+        mTransactionCallbackInvoker.addEmptyTransaction(listener);
     }
 
-    std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |=
-                setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
-                                     postTime, permissions, listenerCallbacksWithSurfaces);
+        clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime,
+                                                 isAutoTimestamp, postTime, permissions);
         if ((flags & eAnimation) && state.state.surface) {
-            if (const auto layer = fromHandle(state.state.surface).promote(); layer) {
+            if (const auto layer = fromHandle(state.state.surface).promote()) {
+                using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
                 mScheduler->recordLayerHistory(layer.get(),
                                                isAutoTimestamp ? 0 : desiredPresentTime,
-                                               LayerHistory::LayerUpdateType::AnimationTX);
+                                               LayerUpdateType::AnimationTX);
             }
         }
     }
 
-    for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
-        mTransactionCallbackInvoker.endRegistration(listenerCallback);
-    }
-
-    // If the state doesn't require a traversal and there are callbacks, send them now
-    if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
-        mTransactionCallbackInvoker.sendCallbacks();
-    }
     transactionFlags |= clientStateFlags;
 
     if (permissions & Permission::ACCESS_SURFACE_FLINGER) {
@@ -3821,6 +4054,7 @@
         transactionFlags = eTransactionNeeded;
     }
 
+    bool needsTraversal = false;
     if (transactionFlags) {
         if (mInterceptor->isEnabled()) {
             mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
@@ -3831,7 +4065,7 @@
         // so we don't have to wake up again next frame to preform an unnecessary traversal.
         if (transactionFlags & eTraversalNeeded) {
             transactionFlags = transactionFlags & (~eTraversalNeeded);
-            mForceTraversal = true;
+            needsTraversal = true;
         }
         if (transactionFlags) {
             setTransactionFlags(transactionFlags);
@@ -3841,6 +4075,8 @@
             mAnimTransactionPending = true;
         }
     }
+
+    return needsTraversal;
 }
 
 uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) {
@@ -3909,10 +4145,10 @@
     return true;
 }
 
-uint32_t SurfaceFlinger::setClientStateLocked(
-        const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
-        int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
-        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& outListenerCallbacks) {
+uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
+                                              const ComposerState& composerState,
+                                              int64_t desiredPresentTime, bool isAutoTimestamp,
+                                              int64_t postTime, uint32_t permissions) {
     const layer_state_t& s = composerState.state;
     const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
 
@@ -3925,16 +4161,12 @@
 
         ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
         if (!onCommitCallbacks.callbackIds.empty()) {
-            mTransactionCallbackInvoker.startRegistration(onCommitCallbacks);
             filteredListeners.push_back(onCommitCallbacks);
-            outListenerCallbacks.insert(onCommitCallbacks);
         }
 
         ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
         if (!onCompleteCallbacks.callbackIds.empty()) {
-            mTransactionCallbackInvoker.startRegistration(onCompleteCallbacks);
             filteredListeners.push_back(onCompleteCallbacks);
-            outListenerCallbacks.insert(onCompleteCallbacks);
         }
     }
 
@@ -4064,8 +4296,15 @@
             flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eFlagsChanged) {
-        if (layer->setFlags(s.flags, s.mask))
-            flags |= eTraversalNeeded;
+        auto changedFlags = s.flags;
+        if (changedFlags & layer_state_t::eLayerIsDisplayDecoration) {
+            if ((permissions & Permission::INTERNAL_SYSTEM_WINDOW) == 0) {
+                changedFlags &= ~layer_state_t::eLayerIsDisplayDecoration;
+                ALOGE("Attempt to use eLayerIsDisplayDecoration without permission "
+                      "INTERNAL_SYSTEM_WINDOW!");
+            }
+        }
+        if (layer->setFlags(changedFlags, s.mask)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eCornerRadiusChanged) {
         if (layer->setCornerRadius(s.cornerRadius))
@@ -4107,9 +4346,6 @@
     if (what & layer_state_t::eCropChanged) {
         if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eAcquireFenceChanged) {
-        if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
-    }
     if (what & layer_state_t::eDataspaceChanged) {
         if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
     }
@@ -4136,13 +4372,14 @@
     std::optional<nsecs_t> dequeueBufferTimestamp;
     if (what & layer_state_t::eMetadataChanged) {
         dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
-        auto gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1);
-        if (gameMode != -1) {
+
+        if (const int32_t gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); gameMode != -1) {
             // The transaction will be received on the Task layer and needs to be applied to all
             // child layers. Child layers that are added at a later point will obtain the game mode
             // info through addChild().
-            layer->setGameModeForTree(gameMode);
+            layer->setGameModeForTree(static_cast<GameMode>(gameMode));
         }
+
         if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eColorSpaceAgnosticChanged) {
@@ -4166,7 +4403,8 @@
             const auto strategy =
                     Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
 
-            if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
+            if (layer->setFrameRate(
+                        Layer::FrameRate(Fps::fromValue(s.frameRate), compatibility, strategy))) {
                 flags |= eTraversalNeeded;
             }
         }
@@ -4210,7 +4448,7 @@
                 mInputInfoChanged = true;
             }
         } else {
-            ALOGE("Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER");
+            ALOGE("Attempt to update DropInputMode without permission ACCESS_SURFACE_FLINGER");
         }
     }
     // This has to happen after we reparent children because when we reparent to null we remove
@@ -4236,43 +4474,11 @@
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
-    bool bufferChanged = what & layer_state_t::eBufferChanged;
-    bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
-    bool bufferSizeExceedsLimit = false;
-    std::shared_ptr<renderengine::ExternalTexture> buffer;
-    if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
-        bufferSizeExceedsLimit =
-                exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
-        if (!bufferSizeExceedsLimit) {
-            ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
-            buffer = ClientCache::getInstance().get(s.cachedBuffer);
-        }
-    } else if (cacheIdChanged) {
-        buffer = ClientCache::getInstance().get(s.cachedBuffer);
-    } else if (bufferChanged && s.buffer != nullptr) {
-        bufferSizeExceedsLimit =
-                exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
-        if (!bufferSizeExceedsLimit) {
-            buffer = std::make_shared<
-                    renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
-                                                   renderengine::ExternalTexture::Usage::READABLE);
-        }
-    }
-    ALOGE_IF(bufferSizeExceedsLimit,
-             "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
-             "limit.",
-             layer->getDebugName());
-    if (buffer) {
-        const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
-        const uint64_t frameNumber = frameNumberChanged
-                ? s.frameNumber
-                : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
 
-        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
-                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo,
-                             s.releaseBufferListener, s.releaseBufferEndpoint)) {
-            flags |= eTraversalNeeded;
-        }
+    if (what & layer_state_t::eBufferChanged &&
+        layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
+                         dequeueBufferTimestamp, frameTimelineInfo)) {
+        flags |= eTraversalNeeded;
     } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
         layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
@@ -4288,25 +4494,22 @@
     return hasChanges ? eTraversalNeeded : 0;
 }
 
-status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
-                                     sp<IBinder>* outHandle, int32_t* outLayerId) {
+status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args,
+                                     const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+                                     int32_t* outLayerId) {
     if (!mirrorFromHandle) {
         return NAME_NOT_FOUND;
     }
 
     sp<Layer> mirrorLayer;
     sp<Layer> mirrorFrom;
-    std::string uniqueName = getUniqueLayerName("MirrorRoot");
-
     {
         Mutex::Autolock _l(mStateLock);
         mirrorFrom = fromHandle(mirrorFromHandle).promote();
         if (!mirrorFrom) {
             return NAME_NOT_FOUND;
         }
-
-        status_t result = createContainerLayer(client, std::move(uniqueName), -1, -1, 0,
-                                               LayerMetadata(), outHandle, &mirrorLayer);
+        status_t result = createContainerLayer(args, outHandle, &mirrorLayer);
         if (result != NO_ERROR) {
             return result;
         }
@@ -4315,22 +4518,17 @@
     }
 
     *outLayerId = mirrorLayer->sequence;
-    return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
-                          nullptr /* outTransformHint */);
+    if (mTransactionTracingEnabled) {
+        mTransactionTracing.onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
+                                               args.name, mirrorFrom->sequence);
+    }
+    return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
+                          false /* addAsRoot */, nullptr /* outTransformHint */);
 }
 
-status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
-                                     uint32_t h, PixelFormat format, uint32_t flags,
-                                     LayerMetadata metadata, sp<IBinder>* handle,
-                                     sp<IGraphicBufferProducer>* gbp,
+status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
                                      const sp<IBinder>& parentHandle, int32_t* outLayerId,
                                      const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
-    if (int32_t(w|h) < 0) {
-        ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
-                int(w), int(h));
-        return BAD_VALUE;
-    }
-
     ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
             "Expected only one of parentLayer or parentHandle to be non-null. "
             "Programmer error?");
@@ -4339,40 +4537,22 @@
 
     sp<Layer> layer;
 
-    std::string uniqueName = getUniqueLayerName(name.string());
-
-    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
+    switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
         case ISurfaceComposerClient::eFXSurfaceBufferState: {
-            result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
-                                            std::move(metadata), handle, &layer);
+            result = createBufferStateLayer(args, outHandle, &layer);
             std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
             if (pendingBufferCounter) {
                 std::string counterName = layer->getPendingBufferCounterName();
-                mBufferCountTracker.add((*handle)->localBinder(), counterName,
+                mBufferCountTracker.add((*outHandle)->localBinder(), counterName,
                                         pendingBufferCounter);
             }
         } break;
         case ISurfaceComposerClient::eFXSurfaceEffect:
-            // check if buffer size is set for color layer.
-            if (w > 0 || h > 0) {
-                ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
-                      int(w), int(h));
-                return BAD_VALUE;
-            }
-
-            result = createEffectLayer(client, std::move(uniqueName), w, h, flags,
-                                       std::move(metadata), handle, &layer);
+            result = createEffectLayer(args, outHandle, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceContainer:
-            // check if buffer size is set for container layer.
-            if (w > 0 || h > 0) {
-                ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
-                      int(w), int(h));
-                return BAD_VALUE;
-            }
-            result = createContainerLayer(client, std::move(uniqueName), w, h, flags,
-                                          std::move(metadata), handle, &layer);
+            result = createContainerLayer(args, outHandle, &layer);
             break;
         default:
             result = BAD_VALUE;
@@ -4384,46 +4564,38 @@
     }
 
     bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess();
-    result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer, addToRoot,
-                            outTransformHint);
+    wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
+    if (parentHandle != nullptr && parent == nullptr) {
+        ALOGE("Invalid parent handle %p.", parentHandle.get());
+        addToRoot = false;
+    }
+    if (parentLayer != nullptr) {
+        addToRoot = false;
+    }
+
+    int parentId = -1;
+    // We can safely promote the layer in binder thread because we have a strong reference
+    // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
+    sp<Layer> parentSp = parent.promote();
+    if (parentSp != nullptr) {
+        parentId = parentSp->getSequence();
+    }
+    if (mTransactionTracingEnabled) {
+        mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+                                         args.flags, parentId);
+    }
+
+    result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
     if (result != NO_ERROR) {
         return result;
     }
-    mInterceptor->saveSurfaceCreation(layer);
 
     setTransactionFlags(eTransactionNeeded);
     *outLayerId = layer->sequence;
     return result;
 }
 
-std::string SurfaceFlinger::getUniqueLayerName(const char* name) {
-    unsigned dupeCounter = 0;
-
-    // Tack on our counter whether there is a hit or not, so everyone gets a tag
-    std::string uniqueName = base::StringPrintf("%s#%u", name, dupeCounter);
-
-    // Grab the state lock since we're accessing mCurrentState
-    Mutex::Autolock lock(mStateLock);
-
-    // Loop over layers until we're sure there is no matching name
-    bool matchFound = true;
-    while (matchFound) {
-        matchFound = false;
-        mCurrentState.traverse([&](Layer* layer) {
-            if (layer->getName() == uniqueName) {
-                matchFound = true;
-                uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
-            }
-        });
-    }
-
-    ALOGV_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name, uniqueName.c_str());
-    return uniqueName;
-}
-
-status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::string name,
-                                                uint32_t w, uint32_t h, uint32_t flags,
-                                                LayerMetadata metadata, PixelFormat& format,
+status_t SurfaceFlinger::createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
                                                 sp<IBinder>* handle,
                                                 sp<IGraphicBufferProducer>* gbp,
                                                 sp<Layer>* outLayer) {
@@ -4439,7 +4611,6 @@
     }
 
     sp<BufferQueueLayer> layer;
-    LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
     args.textureName = getNewTexture();
     {
         // Grab the SF state lock during this since it's the only safe way to access
@@ -4449,7 +4620,7 @@
         layer = getFactory().createBufferQueueLayer(args);
     }
 
-    status_t err = layer->setDefaultBufferProperties(w, h, format);
+    status_t err = layer->setDefaultBufferProperties(0, 0, format);
     if (err == NO_ERROR) {
         *handle = layer->getHandle();
         *gbp = layer->getProducer();
@@ -4460,34 +4631,24 @@
     return err;
 }
 
-status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
-                                                uint32_t w, uint32_t h, uint32_t flags,
-                                                LayerMetadata metadata, sp<IBinder>* handle,
+status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
                                                 sp<Layer>* outLayer) {
-    LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
     args.textureName = getNewTexture();
-    sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
-    *handle = layer->getHandle();
-    *outLayer = layer;
-
-    return NO_ERROR;
-}
-
-status_t SurfaceFlinger::createEffectLayer(const sp<Client>& client, std::string name, uint32_t w,
-                                           uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                           sp<IBinder>* handle, sp<Layer>* outLayer) {
-    *outLayer = getFactory().createEffectLayer(
-            {this, client, std::move(name), w, h, flags, std::move(metadata)});
+    *outLayer = getFactory().createBufferStateLayer(args);
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, std::string name,
-                                              uint32_t w, uint32_t h, uint32_t flags,
-                                              LayerMetadata metadata, sp<IBinder>* handle,
+status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
+                                           sp<Layer>* outLayer) {
+    *outLayer = getFactory().createEffectLayer(args);
+    *handle = (*outLayer)->getHandle();
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
                                               sp<Layer>* outLayer) {
-    *outLayer = getFactory().createContainerLayer(
-            {this, client, std::move(name), w, h, flags, std::move(metadata)});
+    *outLayer = getFactory().createContainerLayer(args);
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
@@ -4511,6 +4672,9 @@
     markLayerPendingRemovalLocked(layer);
     mBufferCountTracker.remove(handle);
     layer.clear();
+    if (mTransactionTracingEnabled) {
+        mTransactionTracing.onHandleRemoved(handle);
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -4529,7 +4693,7 @@
     d.what = DisplayState::eDisplayProjectionChanged |
              DisplayState::eLayerStackChanged;
     d.token = token;
-    d.layerStack = 0;
+    d.layerStack = ui::DEFAULT_LAYER_STACK;
     d.orientation = ui::ROTATION_0;
     d.orientedDisplaySpaceRect.makeInvalid();
     d.layerStackSpaceRect.makeInvalid();
@@ -4538,10 +4702,12 @@
     displays.add(d);
 
     nsecs_t now = systemTime();
+
+    int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
     // It should be on the main thread, apply it directly.
     applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
                           /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
-                          {}, getpid(), getuid(), 0 /* Undefined transactionId */);
+                          {}, mPid, getuid(), transactionId);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
     const nsecs_t vsyncPeriod =
@@ -4556,27 +4722,7 @@
 
 void SurfaceFlinger::initializeDisplays() {
     // Async since we may be called from the main thread.
-    static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
-}
-
-sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const {
-    sp<DisplayDevice> display;
-    for (const auto& pair : mDisplays) {
-        const auto& displayDevice = pair.second;
-        if (!displayDevice->receivesInput() ||
-            !displayDevice->getCompositionDisplay()
-                     ->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
-            continue;
-        }
-        // Don't return immediately so that we can log duplicates.
-        if (display) {
-            ALOGE("Multiple display devices claim to accept input for the same layerstack: %d",
-                  layer->getLayerStack());
-            continue;
-        }
-        display = displayDevice;
-    }
-    return display;
+    static_cast<void>(mScheduler->schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4627,7 +4773,7 @@
 
         mVisibleRegionsDirty = true;
         mHasPoweredOff = true;
-        repaintEverything();
+        scheduleComposite(FrameHint::kActive);
     } else if (mode == hal::PowerMode::OFF) {
         // Turn off the display
         if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
@@ -4676,7 +4822,7 @@
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    schedule([=]() MAIN_THREAD {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4686,7 +4832,9 @@
         } else {
             setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
         }
-    }).wait();
+    });
+
+    future.wait();
 }
 
 status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
@@ -4736,7 +4884,7 @@
         }
 
         if (dumpLayers) {
-            LayersTraceFileProto traceFileProto = SurfaceTracing::createLayersTraceFileProto();
+            LayersTraceFileProto traceFileProto = mLayerTracing.createTraceFileProto();
             LayersTraceProto* layersTrace = traceFileProto.add_entry();
             LayersProto layersProto = dumpProtoFromMainThread();
             layersTrace->mutable_layers()->Swap(&layersProto);
@@ -4758,8 +4906,9 @@
 }
 
 status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
-    if (asProto && mTracing.isEnabled()) {
-        mTracing.writeToFile();
+    if (asProto) {
+        mLayerTracing.writeToFile();
+        mTransactionTracing.writeToFile();
     }
 
     return doDump(fd, DumpArgs(), asProto);
@@ -4871,24 +5020,6 @@
                   bucketTimeSec, percent);
 }
 
-void SurfaceFlinger::recordBufferingStats(const std::string& layerName,
-                                          std::vector<OccupancyTracker::Segment>&& history) {
-    Mutex::Autolock lock(getBE().mBufferingStatsMutex);
-    auto& stats = getBE().mBufferingStats[layerName];
-    for (const auto& segment : history) {
-        if (!segment.usedThirdBuffer) {
-            stats.twoBufferTime += segment.totalTime;
-        }
-        if (segment.occupancyAverage < 1.0f) {
-            stats.doubleBufferedTime += segment.totalTime;
-        } else if (segment.occupancyAverage < 2.0f) {
-            stats.tripleBufferedTime += segment.totalTime;
-        }
-        ++stats.numSegments;
-        stats.totalTime += segment.totalTime;
-    }
-}
-
 void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
     result.append("Layer frame timestamps:\n");
     // Traverse all layers to dump frame-events for each layer
@@ -4896,38 +5027,6 @@
         [&] (Layer* layer) { layer->dumpFrameEvents(result); });
 }
 
-void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
-    result.append("Buffering stats:\n");
-    result.append("  [Layer name] <Active time> <Two buffer> "
-            "<Double buffered> <Triple buffered>\n");
-    Mutex::Autolock lock(getBE().mBufferingStatsMutex);
-    typedef std::tuple<std::string, float, float, float> BufferTuple;
-    std::map<float, BufferTuple, std::greater<float>> sorted;
-    for (const auto& statsPair : getBE().mBufferingStats) {
-        const char* name = statsPair.first.c_str();
-        const SurfaceFlingerBE::BufferingStats& stats = statsPair.second;
-        if (stats.numSegments == 0) {
-            continue;
-        }
-        float activeTime = ns2ms(stats.totalTime) / 1000.0f;
-        float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
-                stats.totalTime;
-        float doubleBufferRatio = static_cast<float>(
-                stats.doubleBufferedTime) / stats.totalTime;
-        float tripleBufferRatio = static_cast<float>(
-                stats.tripleBufferedTime) / stats.totalTime;
-        sorted.insert({activeTime, {name, twoBufferRatio,
-                doubleBufferRatio, tripleBufferRatio}});
-    }
-    for (const auto& sortedPair : sorted) {
-        float activeTime = sortedPair.first;
-        const BufferTuple& values = sortedPair.second;
-        StringAppendF(&result, "  [%s] %.2f %.3f %.3f %.3f\n", std::get<0>(values).c_str(),
-                      activeTime, std::get<1>(values), std::get<2>(values), std::get<3>(values));
-    }
-    result.append("\n");
-}
-
 void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
         const auto displayId = PhysicalDisplayId::tryCast(display->getId());
@@ -5006,7 +5105,6 @@
 }
 
 LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
-    // If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread.
     const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
 
     LayersProto layersProto;
@@ -5022,7 +5120,7 @@
         DisplayProto* displayProto = layersTraceProto.add_displays();
         displayProto->set_id(display->getId().value);
         displayProto->set_name(display->getDisplayName());
-        displayProto->set_layer_stack(display->getLayerStack());
+        displayProto->set_layer_stack(display->getLayerStack().id);
         LayerProtoHelper::writeSizeToProto(display->getWidth(), display->getHeight(),
                                            [&]() { return displayProto->mutable_size(); });
         LayerProtoHelper::writeToProto(display->getLayerStackSpaceRect(), [&]() {
@@ -5030,6 +5128,7 @@
         });
         LayerProtoHelper::writeTransformToProto(display->getTransform(),
                                                 displayProto->mutable_transform());
+        displayProto->set_is_virtual(display->isVirtual());
     }
 }
 
@@ -5058,21 +5157,21 @@
 }
 
 LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
-    return schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
+    return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
 }
 
 void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
+    auto future = mScheduler->schedule([this] {
+        std::string result;
+        for (Layer* offscreenLayer : mOffscreenLayers) {
+            offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+                                     [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+        }
+        return result;
+    });
+
     result.append("Offscreen Layers:\n");
-    result.append(schedule([this] {
-                      std::string result;
-                      for (Layer* offscreenLayer : mOffscreenLayers) {
-                          offscreenLayer->traverse(LayerVector::StateSet::Drawing,
-                                                   [&](Layer* layer) {
-                                                       layer->dumpCallingUidPid(result);
-                                                   });
-                      }
-                      return result;
-                  }).get());
+    result.append(future.get());
 }
 
 void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
@@ -5119,15 +5218,11 @@
     StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
     StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
 
-    dumpBufferingStats(result);
-
     /*
      * Dump the visible layer list
      */
     colorizer.bold(result);
     StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
-    StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n",
-                  mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
     colorizer.reset(result);
 
     {
@@ -5209,7 +5304,9 @@
     /*
      * Tracing state
      */
-    mTracing.dump(result);
+    mLayerTracing.dump(result);
+    result.append("\n");
+    mTransactionTracing.dump(result);
     result.append("\n");
 
     /*
@@ -5243,7 +5340,7 @@
     colorizer.bold(result);
     result.append("h/w composer state:\n");
     colorizer.reset(result);
-    bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
+    const bool hwcDisabled = mDebugDisableHWC || mDebugFlashDelay;
     StringAppendF(&result, "  h/w composer %s\n", hwcDisabled ? "disabled" : "enabled");
     getHwComposer().dump(result);
 
@@ -5253,6 +5350,11 @@
     const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
     alloc.dump(result);
 
+    /*
+     * Dump flag/property manager state
+     */
+    mFlagManager->dump(result);
+
     result.append(mTimeStats->miniDump());
     result.append("\n");
 }
@@ -5316,11 +5418,9 @@
         case SET_GLOBAL_SHADOW_SETTINGS:
         case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
-            // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
-            // which acquire the necessary permission dynamically. Don't use the permission cache
-            // for this check.
-            bool usePermissionCache =
-                    code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES;
+            // OVERRIDE_HDR_TYPES is used by CTS tests, which acquire the necessary
+            // permission dynamically. Don't use the permission cache for this check.
+            bool usePermissionCache = code != OVERRIDE_HDR_TYPES;
             if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5369,6 +5469,7 @@
         // special permissions.
         case SET_FRAME_RATE:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
+        case GET_DISPLAY_DECORATION_SUPPORT:
         // captureLayers and captureDisplay will handle the permission check in the function
         case CAPTURE_LAYERS:
         case CAPTURE_DISPLAY:
@@ -5440,9 +5541,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1040 are currently used for backdoors. The code
+    // Numbers from 1000 to 1041 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1040) {
+    if (code >= 1000 && code <= 1041) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5453,9 +5554,8 @@
 
 status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                     uint32_t flags) {
-    status_t credentialCheck = CheckTransactCodeCredentials(code);
-    if (credentialCheck != OK) {
-        return credentialCheck;
+    if (const status_t error = CheckTransactCodeCredentials(code); error != OK) {
+        return error;
     }
 
     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
@@ -5472,47 +5572,41 @@
         }
         int n;
         switch (code) {
-            case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
-            case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
+            case 1000: // Unused.
+            case 1001:
+                return NAME_NOT_FOUND;
+            case 1002: // Toggle flashing on surface damage.
+                if (const int delay = data.readInt32(); delay > 0) {
+                    mDebugFlashDelay = delay;
+                } else {
+                    mDebugFlashDelay = mDebugFlashDelay ? 0 : 1;
+                }
+                scheduleRepaint();
                 return NO_ERROR;
-            case 1002:  // SHOW_UPDATES
-                n = data.readInt32();
-                mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
-                invalidateHwcGeometry();
-                repaintEverything();
+            case 1004: // Force composite ahead of next VSYNC.
+            case 1006:
+                scheduleComposite(FrameHint::kActive);
                 return NO_ERROR;
-            case 1004:{ // repaint everything
-                repaintEverything();
+            case 1005: { // Force commit ahead of next VSYNC.
+                Mutex::Autolock lock(mStateLock);
+                setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded |
+                                    eTraversalNeeded);
                 return NO_ERROR;
             }
-            case 1005:{ // force transaction
-                Mutex::Autolock _l(mStateLock);
-                setTransactionFlags(
-                        eTransactionNeeded|
-                        eDisplayTransactionNeeded|
-                        eTraversalNeeded);
+            case 1007: // Unused.
+                return NAME_NOT_FOUND;
+            case 1008: // Toggle forced GPU composition.
+                mDebugDisableHWC = data.readInt32() != 0;
+                scheduleRepaint();
                 return NO_ERROR;
-            }
-            case 1006:{ // send empty update
-                signalRefresh();
+            case 1009: // Toggle use of transform hint.
+                mDebugDisableTransformHint = data.readInt32() != 0;
+                scheduleRepaint();
                 return NO_ERROR;
-            }
-            case 1008:  // toggle use of hw composer
-                n = data.readInt32();
-                mDebugDisableHWC = n != 0;
-                invalidateHwcGeometry();
-                repaintEverything();
-                return NO_ERROR;
-            case 1009:  // toggle use of transform hint
-                n = data.readInt32();
-                mDebugDisableTransformHint = n != 0;
-                invalidateHwcGeometry();
-                repaintEverything();
-                return NO_ERROR;
-            case 1010:  // interrogate.
+            case 1010: // Interrogate.
                 reply->writeInt32(0);
                 reply->writeInt32(0);
-                reply->writeInt32(mDebugRegion);
+                reply->writeInt32(mDebugFlashDelay);
                 reply->writeInt32(0);
                 reply->writeInt32(mDebugDisableHWC);
                 return NO_ERROR;
@@ -5609,7 +5703,8 @@
             }
             case 1021: { // Disable HWC virtual displays
                 const bool enable = data.readInt32() != 0;
-                static_cast<void>(schedule([this, enable] { enableHalVirtualDisplays(enable); }));
+                static_cast<void>(
+                        mScheduler->schedule([this, enable] { enableHalVirtualDisplays(enable); }));
                 return NO_ERROR;
             }
             case 1022: { // Set saturation boost
@@ -5626,8 +5721,7 @@
                 if (data.readInt32(&colorMode) == NO_ERROR) {
                     mForceColorMode = static_cast<ColorMode>(colorMode);
                 }
-                invalidateHwcGeometry();
-                repaintEverything();
+                scheduleRepaint();
                 return NO_ERROR;
             }
             // Deprecate, use 1030 to check whether the device is color managed.
@@ -5639,20 +5733,21 @@
                 bool tracingEnabledChanged;
                 if (n) {
                     ALOGD("LayerTracing enabled");
-                    tracingEnabledChanged = mTracing.enable();
+                    tracingEnabledChanged = mLayerTracing.enable();
                     if (tracingEnabledChanged) {
-                        schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait();
+                        mScheduler->schedule([&]() MAIN_THREAD { mLayerTracing.notify("start"); })
+                                .wait();
                     }
                 } else {
                     ALOGD("LayerTracing disabled");
-                    tracingEnabledChanged = mTracing.disable();
+                    tracingEnabledChanged = mLayerTracing.disable();
                 }
                 mTracingEnabledChanged = tracingEnabledChanged;
                 reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
             }
             case 1026: { // Get layer tracing status
-                reply->writeBool(mTracing.isEnabled());
+                reply->writeBool(mLayerTracing.isEnabled());
                 return NO_ERROR;
             }
             // Is a DisplayColorSetting supported?
@@ -5693,7 +5788,7 @@
                 }
 
                 ALOGD("Updating trace buffer to %d KB", n);
-                mTracing.setBufferSize(n * 1024);
+                mLayerTracing.setBufferSize(n * 1024);
                 reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
             }
@@ -5738,12 +5833,12 @@
             case 1033: {
                 n = data.readUint32();
                 ALOGD("Updating trace flags to 0x%x", n);
-                mTracing.setTraceFlags(n);
+                mLayerTracing.setTraceFlags(n);
                 reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
             }
             case 1034: {
-                schedule([&] {
+                auto future = mScheduler->schedule([&] {
                     switch (n = data.readInt32()) {
                         case 0:
                         case 1:
@@ -5753,64 +5848,78 @@
                             reply->writeBool(ON_MAIN_THREAD(isRefreshRateOverlayEnabled()));
                         }
                     }
-                }).get();
+                });
+
+                future.wait();
                 return NO_ERROR;
             }
             case 1035: {
                 const int modeId = data.readInt32();
-                mDebugDisplayModeSetByBackdoor = false;
 
-                const auto displayId = [&]() -> std::optional<PhysicalDisplayId> {
-                    uint64_t inputDisplayId = 0;
-                    if (data.readUint64(&inputDisplayId) == NO_ERROR) {
-                        const auto token = getPhysicalDisplayToken(
-                                static_cast<PhysicalDisplayId>(inputDisplayId));
-                        if (!token) {
-                            ALOGE("No display with id: %" PRIu64, inputDisplayId);
-                            return std::nullopt;
-                        }
-
-                        return std::make_optional<PhysicalDisplayId>(inputDisplayId);
+                const auto display = [&]() -> sp<IBinder> {
+                    uint64_t value;
+                    if (data.readUint64(&value) != NO_ERROR) {
+                        return getDefaultDisplayDevice()->getDisplayToken().promote();
                     }
 
-                    return getDefaultDisplayDevice()->getPhysicalId();
+                    if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) {
+                        return getPhysicalDisplayToken(*id);
+                    }
+
+                    ALOGE("Invalid physical display ID");
+                    return nullptr;
                 }();
 
-                if (!displayId) {
-                    ALOGE("No display found");
-                    return NO_ERROR;
-                }
-
-                status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId);
-                if (result != NO_ERROR) {
-                    return result;
-                }
-
-                mDebugDisplayModeSetByBackdoor = true;
-
-                return NO_ERROR;
+                mDebugDisplayModeSetByBackdoor = false;
+                const status_t result = setActiveModeFromBackdoor(display, modeId);
+                mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
+                return result;
             }
+            // Turn on/off frame rate flexibility mode. When turned on it overrides the display
+            // manager frame rate policy a new policy which allows switching between all refresh
+            // rates.
             case 1036: {
-                if (data.readInt32() > 0) {
-                    status_t result =
-                            acquireFrameRateFlexibilityToken(&mDebugFrameRateFlexibilityToken);
-                    if (result != NO_ERROR) {
-                        return result;
-                    }
-                } else {
-                    mDebugFrameRateFlexibilityToken = nullptr;
+                if (data.readInt32() > 0) { // turn on
+                    return mScheduler
+                            ->schedule([this] {
+                                const auto display =
+                                        ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+
+                                // This is a little racy, but not in a way that hurts anything. As
+                                // we grab the defaultMode from the display manager policy, we could
+                                // be setting a new display manager policy, leaving us using a stale
+                                // defaultMode. The defaultMode doesn't matter for the override
+                                // policy though, since we set allowGroupSwitching to true, so it's
+                                // not a problem.
+                                scheduler::RefreshRateConfigs::Policy overridePolicy;
+                                overridePolicy.defaultMode = display->refreshRateConfigs()
+                                                                     .getDisplayManagerPolicy()
+                                                                     .defaultMode;
+                                overridePolicy.allowGroupSwitching = true;
+                                constexpr bool kOverridePolicy = true;
+                                return setDesiredDisplayModeSpecsInternal(display, overridePolicy,
+                                                                          kOverridePolicy);
+                            })
+                            .get();
+                } else { // turn off
+                    return mScheduler
+                            ->schedule([this] {
+                                const auto display =
+                                        ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+                                constexpr bool kOverridePolicy = true;
+                                return setDesiredDisplayModeSpecsInternal(display, {},
+                                                                          kOverridePolicy);
+                            })
+                            .get();
                 }
-                return NO_ERROR;
             }
             // Inject a hotplug connected event for the primary display. This will deallocate and
             // reallocate the display state including framebuffers.
             case 1037: {
-                std::optional<hal::HWDisplayId> hwcId;
-                {
-                    Mutex::Autolock lock(mStateLock);
-                    hwcId = getHwComposer().getInternalHwcDisplayId();
-                }
-                onComposerHalHotplug(*hwcId, hal::Connection::CONNECTED);
+                const hal::HWDisplayId hwcId =
+                        (Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId());
+
+                onComposerHalHotplug(hwcId, hal::Connection::CONNECTED);
                 return NO_ERROR;
             }
             // Modify the max number of display frames stored within FrameTimeline
@@ -5845,38 +5954,46 @@
             // Second argument is an optional uint64 - if present, then limits enabling/disabling
             // caching to a particular physical display
             case 1040: {
-                status_t error =
-                        schedule([&] {
-                            n = data.readInt32();
-                            std::optional<PhysicalDisplayId> inputId = std::nullopt;
-                            if (uint64_t inputDisplayId;
-                                data.readUint64(&inputDisplayId) == NO_ERROR) {
-                                const auto token = getPhysicalDisplayToken(
-                                        static_cast<PhysicalDisplayId>(inputDisplayId));
-                                if (!token) {
-                                    ALOGE("No display with id: %" PRIu64, inputDisplayId);
-                                    return NAME_NOT_FOUND;
-                                }
-
-                                inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId);
+                auto future = mScheduler->schedule([&] {
+                    n = data.readInt32();
+                    std::optional<PhysicalDisplayId> inputId = std::nullopt;
+                    if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) {
+                        inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId);
+                        if (!inputId || getPhysicalDisplayToken(*inputId)) {
+                            ALOGE("No display with id: %" PRIu64, inputDisplayId);
+                            return NAME_NOT_FOUND;
+                        }
+                    }
+                    {
+                        Mutex::Autolock lock(mStateLock);
+                        mLayerCachingEnabled = n != 0;
+                        for (const auto& [_, display] : mDisplays) {
+                            if (!inputId || *inputId == display->getPhysicalId()) {
+                                display->enableLayerCaching(mLayerCachingEnabled);
                             }
-                            {
-                                Mutex::Autolock lock(mStateLock);
-                                mLayerCachingEnabled = n != 0;
-                                for (const auto& [_, display] : mDisplays) {
-                                    if (!inputId || *inputId == display->getPhysicalId()) {
-                                        display->enableLayerCaching(mLayerCachingEnabled);
-                                    }
-                                }
-                            }
-                            return OK;
-                        }).get();
+                        }
+                    }
+                    return OK;
+                });
 
-                if (error != OK) {
+                if (const status_t error = future.get(); error != OK) {
                     return error;
                 }
-                invalidateHwcGeometry();
-                repaintEverything();
+                scheduleRepaint();
+                return NO_ERROR;
+            }
+            case 1041: { // Transaction tracing
+                if (data.readInt32()) {
+                    // Transaction tracing is always running but allow the user to temporarily
+                    // increase the buffer when actively debugging.
+                    mTransactionTracing.setBufferSize(
+                            TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
+                } else {
+                    mTransactionTracing.setBufferSize(
+                            TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
+                    mTransactionTracing.writeToFile();
+                }
+                reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
             }
         }
@@ -5884,17 +6001,6 @@
     return err;
 }
 
-void SurfaceFlinger::repaintEverything() {
-    mRepaintEverything = true;
-    signalTransaction();
-}
-
-void SurfaceFlinger::repaintEverythingForHWC() {
-    mRepaintEverything = true;
-    mPowerAdvisor.notifyDisplayUpdateImminent();
-    mEventQueue->invalidate();
-}
-
 void SurfaceFlinger::kernelTimerChanged(bool expired) {
     static bool updateOverlay =
             property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
@@ -5903,7 +6009,7 @@
 
     // Update the overlay on the main thread to avoid race conditions with
     // mRefreshRateConfigs->getCurrentRefreshRate()
-    static_cast<void>(schedule([=] {
+    static_cast<void>(mScheduler->schedule([=] {
         const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
         if (!display) {
             ALOGW("%s: default display is null", __func__);
@@ -5918,7 +6024,7 @@
         const bool timerExpired = mKernelIdleTimerEnabled && expired;
 
         if (display->onKernelTimerChanged(desiredModeId, timerExpired)) {
-            mEventQueue->invalidate();
+            mScheduler->scheduleFrame();
         }
     }));
 }
@@ -6113,12 +6219,13 @@
         traverseLayersInLayerStack(layerStack, args.uid, visitor);
     };
 
-    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
-                               args.pixelFormat, args.allowProtected, args.grayscale,
-                               captureListener);
+    auto captureResultFuture = captureScreenCommon(std::move(renderAreaFuture), traverseLayers,
+                                                   reqSize, args.pixelFormat, args.allowProtected,
+                                                   args.grayscale, captureListener);
+    return captureResultFuture.get().status;
 }
 
-status_t SurfaceFlinger::captureDisplay(uint64_t displayIdOrLayerStack,
+status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
                                         const sp<IScreenCaptureListener>& captureListener) {
     ui::LayerStack layerStack;
     wp<const DisplayDevice> displayWeak;
@@ -6126,21 +6233,14 @@
     ui::Dataspace dataspace;
     {
         Mutex::Autolock lock(mStateLock);
-        auto display = getDisplayDeviceLocked(PhysicalDisplayId{displayIdOrLayerStack});
 
-        // Fall back to first display whose layer stack matches.
-        if (!display) {
-            const auto layerStack = static_cast<ui::LayerStack>(displayIdOrLayerStack);
-            display = findDisplay(WithLayerStack(layerStack));
-        }
-
+        const auto display = getDisplayDeviceLocked(displayId);
         if (!display) {
             return NAME_NOT_FOUND;
         }
 
-        layerStack = display->getLayerStack();
         displayWeak = display;
-
+        layerStack = display->getLayerStack();
         size = display->getLayerStackSpaceRect().getSize();
 
         dataspace =
@@ -6157,9 +6257,15 @@
         traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
     };
 
-    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
-                               ui::PixelFormat::RGBA_8888, false /* allowProtected */,
-                               false /* grayscale */, captureListener);
+    if (captureListener == nullptr) {
+        ALOGE("capture screen must provide a capture listener callback");
+        return BAD_VALUE;
+    }
+    auto captureResultFuture =
+            captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+                                ui::PixelFormat::RGBA_8888, false /* allowProtected */,
+                                false /* grayscale */, captureListener);
+    return captureResultFuture.get().status;
 }
 
 status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
@@ -6229,7 +6335,9 @@
         // colors when capture.
         dataspace = args.dataspace;
         if (dataspace == ui::Dataspace::UNKNOWN) {
-            auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
+            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();
@@ -6277,23 +6385,28 @@
         });
     };
 
-    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
-                               args.pixelFormat, args.allowProtected, args.grayscale,
-                               captureListener);
+    if (captureListener == nullptr) {
+        ALOGE("capture screen must provide a capture listener callback");
+        return BAD_VALUE;
+    }
+
+    auto captureResultFuture = captureScreenCommon(std::move(renderAreaFuture), traverseLayers,
+                                                   reqSize, args.pixelFormat, args.allowProtected,
+                                                   args.grayscale, captureListener);
+    return captureResultFuture.get().status;
 }
 
-status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
-                                             TraverseLayersFunction traverseLayers,
-                                             ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
-                                             bool allowProtected, bool grayscale,
-                                             const sp<IScreenCaptureListener>& captureListener) {
+std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScreenCommon(
+        RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+        ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale,
+        const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
     if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) {
         ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32
               ") that exceeds render target size limit.",
               bufferSize.getWidth(), bufferSize.getHeight());
-        return BAD_VALUE;
+        return ftl::yield<renderengine::RenderEngineResult>({BAD_VALUE, base::unique_fd()}).share();
     }
 
     // Loop over all visible layers to see whether there's any protected layer. A protected layer is
@@ -6303,14 +6416,15 @@
     const bool supportsProtected = getRenderEngine().supportsProtectedContent();
     bool hasProtectedLayer = false;
     if (allowProtected && supportsProtected) {
-        hasProtectedLayer = schedule([=]() {
-                                bool protectedLayerFound = false;
-                                traverseLayers([&](Layer* layer) {
-                                    protectedLayerFound = protectedLayerFound ||
-                                            (layer->isVisible() && layer->isProtected());
-                                });
-                                return protectedLayerFound;
-                            }).get();
+        auto future = mScheduler->schedule([=]() {
+            bool protectedLayerFound = false;
+            traverseLayers([&](Layer* layer) {
+                protectedLayerFound =
+                        protectedLayerFound || (layer->isVisible() && layer->isProtected());
+            });
+            return protectedLayerFound;
+        });
+        hasProtectedLayer = future.get();
     }
 
     const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -6333,50 +6447,67 @@
                                false /* regionSampling */, grayscale, captureListener);
 }
 
-status_t SurfaceFlinger::captureScreenCommon(
+std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScreenCommon(
         RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
-    if (captureListener == nullptr) {
-        ALOGE("capture screen must provide a capture listener callback");
-        return BAD_VALUE;
-    }
-
     bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
 
-    static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
-        if (mRefreshPending) {
-            ALOGW("Skipping screenshot for now");
-            captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling,
-                                grayscale, captureListener);
-            return;
-        }
+    auto scheduleResultFuture = mScheduler->schedule([=,
+                                                      renderAreaFuture =
+                                                              std::move(renderAreaFuture)]() mutable
+                                                     -> std::shared_future<
+                                                             renderengine::RenderEngineResult> {
         ScreenCaptureResults captureResults;
         std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
         if (!renderArea) {
             ALOGW("Skipping screen capture because of invalid render area.");
             captureResults.result = NO_MEMORY;
             captureListener->onScreenCaptureCompleted(captureResults);
-            return;
+            return ftl::yield<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})
+                    .share();
         }
 
-        status_t result = NO_ERROR;
+        std::shared_future<renderengine::RenderEngineResult> renderEngineResultFuture;
+
         renderArea->render([&] {
-            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
-                                            canCaptureBlackoutContent, regionSampling, grayscale,
-                                            captureResults);
+            renderEngineResultFuture =
+                    renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+                                           canCaptureBlackoutContent, regionSampling, grayscale,
+                                           captureResults);
         });
+        // spring up a thread to unblock SF main thread and wait for
+        // RenderEngineResult to be available
+        if (captureListener != nullptr) {
+            std::async([=]() mutable {
+                ATRACE_NAME("captureListener is nonnull!");
+                auto& [status, drawFence] = renderEngineResultFuture.get();
+                captureResults.result = status;
+                captureResults.fence = new Fence(dup(drawFence));
+                captureListener->onScreenCaptureCompleted(captureResults);
+            });
+        }
+        return renderEngineResultFuture;
+    });
 
-        captureResults.result = result;
-        captureListener->onScreenCaptureCompleted(captureResults);
-    }));
-
-    return NO_ERROR;
+    // flatten scheduleResultFuture object to single shared_future object
+    if (captureListener == nullptr) {
+        std::future<renderengine::RenderEngineResult> captureScreenResultFuture =
+                ftl::chain(std::move(scheduleResultFuture))
+                        .then([=](std::shared_future<renderengine::RenderEngineResult> futureObject)
+                                      -> renderengine::RenderEngineResult {
+                            auto& [status, drawFence] = futureObject.get();
+                            return {status, base::unique_fd(dup(drawFence))};
+                        });
+        return captureScreenResultFuture.share();
+    } else {
+        return ftl::yield<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}).share();
+    }
 }
 
-status_t SurfaceFlinger::renderScreenImplLocked(
+std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScreenImplLocked(
         const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer,
         bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
@@ -6395,7 +6526,8 @@
     // the impetus on WindowManager to not persist them.
     if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
         ALOGW("FB is protected: PERMISSION_DENIED");
-        return PERMISSION_DENIED;
+        return ftl::yield<renderengine::RenderEngineResult>({PERMISSION_DENIED, base::unique_fd()})
+                .share();
     }
 
     captureResults.buffer = buffer->getBuffer();
@@ -6435,7 +6567,6 @@
 
     const auto display = renderArea.getDisplayDevice();
     std::vector<Layer*> renderedLayers;
-    Region clearRegion = Region::INVALID_REGION;
     bool disableBlurs = false;
     traverseLayers([&](Layer* layer) {
         disableBlurs |= layer->getDrawingState().sidebandStream != nullptr;
@@ -6447,7 +6578,6 @@
                         renderArea.needsFiltering(),
                 renderArea.isSecure(),
                 useProtected,
-                clearRegion,
                 layerStackSpaceRect,
                 clientCompositionDisplay.outputDataspace,
                 true,  /* realContentIsVisible */
@@ -6456,6 +6586,8 @@
                                        BlurSetting::Disabled
                              : compositionengine::LayerFE::ClientCompositionTargetSettings::
                                        BlurSetting::Enabled,
+                DisplayDevice::sDefaultMaxLumiance,
+
         };
         std::vector<compositionengine::LayerFE::LayerSettings> results =
                 layer->prepareClientCompositionList(targetSettings);
@@ -6479,35 +6611,35 @@
 
     });
 
-    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers(
-            clientCompositionLayers.size());
+    std::vector<renderengine::LayerSettings> clientRenderEngineLayers;
+    clientRenderEngineLayers.reserve(clientCompositionLayers.size());
     std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
-                   clientCompositionLayerPointers.begin(),
-                   std::pointer_traits<renderengine::LayerSettings*>::pointer_to);
+                   std::back_inserter(clientRenderEngineLayers),
+                   [](compositionengine::LayerFE::LayerSettings& settings)
+                           -> renderengine::LayerSettings { return settings; });
 
-    clientCompositionDisplay.clearRegion = clearRegion;
     // Use an empty fence for the buffer fence, since we just created the buffer so
     // there is no need for synchronization with the GPU.
     base::unique_fd bufferFence;
-    base::unique_fd drawFence;
     getRenderEngine().useProtectedContext(useProtected);
 
     const constexpr bool kUseFramebufferCache = false;
-    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
-                                 kUseFramebufferCache, std::move(bufferFence), &drawFence);
+    std::future<renderengine::RenderEngineResult> drawLayersResult =
+            getRenderEngine().drawLayers(clientCompositionDisplay, clientRenderEngineLayers, buffer,
+                                         kUseFramebufferCache, std::move(bufferFence));
 
-    if (drawFence >= 0) {
-        sp<Fence> releaseFence = new Fence(dup(drawFence));
-        for (auto* layer : renderedLayers) {
-            layer->onLayerDisplayed(releaseFence);
-        }
+    std::shared_future<renderengine::RenderEngineResult> drawLayersResultFuture =
+            drawLayersResult.share(); // drawLayersResult will be moved to shared one
+
+    for (auto* layer : renderedLayers) {
+        // make a copy of shared_future object for each layer
+        layer->onLayerDisplayed(drawLayersResultFuture);
     }
 
-    captureResults.fence = new Fence(drawFence.release());
     // Always switch back to unprotected context.
     getRenderEngine().useProtectedContext(false);
 
-    return NO_ERROR;
+    return drawLayersResultFuture;
 }
 
 void SurfaceFlinger::windowInfosReported() {
@@ -6534,12 +6666,12 @@
     // We loop through the first level of layers without traversing,
     // as we need to determine which layers belong to the requested display.
     for (const auto& layer : mDrawingState.layersSortedByZ) {
-        if (!layer->belongsToDisplay(layerStack)) {
+        if (layer->getLayerStack() != layerStack) {
             continue;
         }
         // relative layers are traversed in Layer::traverseInZOrder
         layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
-            if (layer->getPrimaryDisplayOnly()) {
+            if (layer->isInternalDisplayOverlay()) {
                 return;
             }
             if (!layer->isVisible()) {
@@ -6603,7 +6735,7 @@
     if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
         ALOGV("switching to Scheduler preferred display mode %d",
               preferredDisplayMode->getId().value());
-        setDesiredActiveMode({preferredDisplayMode, Scheduler::ModeEvent::Changed});
+        setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed});
     } else {
         LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
                          preferredDisplayMode->getId().value());
@@ -6622,7 +6754,7 @@
         return BAD_VALUE;
     }
 
-    auto future = schedule([=]() -> status_t {
+    auto future = mScheduler->schedule([=]() -> status_t {
         const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
         if (!display) {
             ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -6635,8 +6767,10 @@
             using Policy = scheduler::RefreshRateConfigs::Policy;
             const Policy policy{DisplayModeId(defaultMode),
                                 allowGroupSwitching,
-                                {Fps(primaryRefreshRateMin), Fps(primaryRefreshRateMax)},
-                                {Fps(appRequestRefreshRateMin), Fps(appRequestRefreshRateMax)}};
+                                {Fps::fromValue(primaryRefreshRateMin),
+                                 Fps::fromValue(primaryRefreshRateMax)},
+                                {Fps::fromValue(appRequestRefreshRateMin),
+                                 Fps::fromValue(appRequestRefreshRateMax)}};
             constexpr bool kOverridePolicy = false;
 
             return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
@@ -6698,6 +6832,13 @@
     if (!layer->isRemovedFromCurrentState()) {
         mScheduler->deregisterLayer(layer);
     }
+    if (mTransactionTracingEnabled) {
+        mTransactionTracing.onLayerRemoved(layer->getSequence());
+    }
+}
+
+void SurfaceFlinger::onLayerUpdate() {
+    scheduleCommit(FrameHint::kActive);
 }
 
 // WARNING: ONLY CALL THIS FROM LAYER DTOR
@@ -6753,7 +6894,7 @@
         return BAD_VALUE;
     }
 
-    static_cast<void>(schedule([=] {
+    static_cast<void>(mScheduler->schedule([=] {
         Mutex::Autolock lock(mStateLock);
         if (authenticateSurfaceTextureLocked(surface)) {
             sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
@@ -6764,7 +6905,7 @@
             const auto strategy =
                     Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
             if (layer->setFrameRate(
-                        Layer::FrameRate(Fps{frameRate},
+                        Layer::FrameRate(Fps::fromValue(frameRate),
                                          Layer::FrameRate::convertCompatibility(compatibility),
                                          strategy))) {
                 setTransactionFlags(eTraversalNeeded);
@@ -6779,74 +6920,6 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
-    if (!outToken) {
-        return BAD_VALUE;
-    }
-
-    auto future = schedule([this] {
-        status_t result = NO_ERROR;
-        sp<IBinder> token;
-
-        if (mFrameRateFlexibilityTokenCount == 0) {
-            const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-
-            // This is a little racy, but not in a way that hurts anything. As we grab the
-            // defaultMode from the display manager policy, we could be setting a new display
-            // manager policy, leaving us using a stale defaultMode. The defaultMode doesn't
-            // matter for the override policy though, since we set allowGroupSwitching to
-            // true, so it's not a problem.
-            scheduler::RefreshRateConfigs::Policy overridePolicy;
-            overridePolicy.defaultMode =
-                    display->refreshRateConfigs().getDisplayManagerPolicy().defaultMode;
-            overridePolicy.allowGroupSwitching = true;
-            constexpr bool kOverridePolicy = true;
-            result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy);
-        }
-
-        if (result == NO_ERROR) {
-            mFrameRateFlexibilityTokenCount++;
-            // Handing out a reference to the SurfaceFlinger object, as we're doing in the line
-            // below, is something to consider carefully. The lifetime of the
-            // FrameRateFlexibilityToken isn't tied to SurfaceFlinger object lifetime, so if this
-            // SurfaceFlinger object were to be destroyed while the token still exists, the token
-            // destructor would be accessing a stale SurfaceFlinger reference, and crash. This is ok
-            // in this case, for two reasons:
-            //   1. Once SurfaceFlinger::run() is called by main_surfaceflinger.cpp, the only way
-            //   the program exits is via a crash. So we won't have a situation where the
-            //   SurfaceFlinger object is dead but the process is still up.
-            //   2. The frame rate flexibility token is acquired/released only by CTS tests, so even
-            //   if condition 1 were changed, the problem would only show up when running CTS tests,
-            //   not on end user devices, so we could spot it and fix it without serious impact.
-            token = new FrameRateFlexibilityToken(
-                    [this]() { onFrameRateFlexibilityTokenReleased(); });
-            ALOGD("Frame rate flexibility token acquired. count=%d",
-                  mFrameRateFlexibilityTokenCount);
-        }
-
-        return std::make_pair(result, token);
-    });
-
-    status_t result;
-    std::tie(result, *outToken) = future.get();
-    return result;
-}
-
-void SurfaceFlinger::onFrameRateFlexibilityTokenReleased() {
-    static_cast<void>(schedule([this] {
-        LOG_ALWAYS_FATAL_IF(mFrameRateFlexibilityTokenCount == 0,
-                            "Failed tracking frame rate flexibility tokens");
-        mFrameRateFlexibilityTokenCount--;
-        ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount);
-        if (mFrameRateFlexibilityTokenCount == 0) {
-            const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-            constexpr bool kOverridePolicy = true;
-            status_t result = setDesiredDisplayModeSpecsInternal(display, {}, kOverridePolicy);
-            LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
-        }
-    }));
-}
-
 status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
                                               const FrameTimelineInfo& frameTimelineInfo) {
     Mutex::Autolock lock(mStateLock);
@@ -6898,33 +6971,29 @@
 }
 
 status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
-    const auto maxSupportedRefreshRate = [&] {
-        const auto display = getDefaultDisplayDevice();
-        if (display) {
-            return display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+    Fps maxRefreshRate = 60_Hz;
+
+    if (!getHwComposer().isHeadless()) {
+        if (const auto display = getDefaultDisplayDevice()) {
+            maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max;
         }
-        ALOGW("%s: default display is null", __func__);
-        return Fps(60);
-    }();
-    *buffers = getMaxAcquiredBufferCountForRefreshRate(maxSupportedRefreshRate);
+    }
+
+    *buffers = getMaxAcquiredBufferCountForRefreshRate(maxRefreshRate);
     return NO_ERROR;
 }
 
-int SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const {
-    const auto refreshRate = [&] {
-        const auto frameRateOverride = mScheduler->getFrameRateOverride(uid);
-        if (frameRateOverride.has_value()) {
-            return frameRateOverride.value();
-        }
+uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const {
+    Fps refreshRate = 60_Hz;
 
-        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-        if (display) {
-            return display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+    if (const auto frameRateOverride = mScheduler->getFrameRateOverride(uid)) {
+        refreshRate = *frameRateOverride;
+    } else if (!getHwComposer().isHeadless()) {
+        if (const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked())) {
+            refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
         }
+    }
 
-        ALOGW("%s: default display is null", __func__);
-        return Fps(60);
-    }();
     return getMaxAcquiredBufferCountForRefreshRate(refreshRate);
 }
 
@@ -6934,7 +7003,7 @@
     return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
 }
 
-void SurfaceFlinger::TransactionState::traverseStatesWithBuffers(
+void TransactionState::traverseStatesWithBuffers(
         std::function<void(const layer_state_t&)> visitor) {
     for (const auto& state : states) {
         if (state.state.hasBufferChanges() && state.state.hasValidBuffer() && state.state.surface) {
@@ -6944,11 +7013,10 @@
 }
 
 void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
-                                          const wp<IBinder>& parent, const wp<Layer> parentLayer,
-                                          const wp<IBinder>& producer, bool addToRoot) {
+                                          const wp<Layer> parent, bool addToRoot) {
     Mutex::Autolock lock(mCreatedLayersLock);
     mCreatedLayers[handle->localBinder()] =
-            std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer, addToRoot);
+            std::make_unique<LayerCreatedState>(layer, parent, addToRoot);
 }
 
 auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
@@ -6986,19 +7054,16 @@
     }
 
     sp<Layer> parent;
-    bool allowAddRoot = state->addToRoot;
+    bool addToRoot = state->addToRoot;
     if (state->initialParent != nullptr) {
-        parent = fromHandle(state->initialParent.promote()).promote();
+        parent = state->initialParent.promote();
         if (parent == nullptr) {
             ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
-            allowAddRoot = false;
+            addToRoot = false;
         }
-    } else if (state->initialParentLayer != nullptr) {
-        parent = state->initialParentLayer.promote();
-        allowAddRoot = false;
     }
 
-    if (parent == nullptr && allowAddRoot) {
+    if (parent == nullptr && addToRoot) {
         layer->setIsAtRoot(true);
         mCurrentState.layersSortedByZ.add(layer);
     } else if (parent == nullptr) {
@@ -7012,32 +7077,16 @@
 
     layer->updateTransformHint(mActiveDisplayTransformHint);
 
-    if (state->initialProducer != nullptr) {
-        mGraphicBufferProducerList.insert(state->initialProducer);
-        LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize,
-                            "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
-                            mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
-                            mNumLayers.load());
-        if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
-            ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
-                  mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
-                  mNumLayers.load());
-        }
-    }
-
+    mInterceptor->saveSurfaceCreation(layer);
     return layer;
 }
 
-void SurfaceFlinger::scheduleRegionSamplingThread() {
-    static_cast<void>(schedule([&] { notifyRegionSamplingThread(); }));
-}
-
-void SurfaceFlinger::notifyRegionSamplingThread() {
+void SurfaceFlinger::sample() {
     if (!mLumaSampling || !mRegionSamplingThread) {
         return;
     }
 
-    mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
+    mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
 }
 
 void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 74fe7d9..61cfb4e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -23,7 +23,6 @@
  */
 
 #include <android-base/thread_annotations.h>
-#include <compositionengine/OutputColorSetting.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <gui/BufferQueue.h>
@@ -32,7 +31,6 @@
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
-#include <gui/OccupancyTracker.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
 #include <renderengine/LayerSettings.h>
@@ -48,13 +46,15 @@
 #include <utils/Trace.h>
 #include <utils/threads.h>
 
+#include <compositionengine/OutputColorSetting.h>
+#include <scheduler/Fps.h>
+
 #include "ClientCache.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
-#include "Fps.h"
 #include "FrameTracker.h"
 #include "LayerVector.h"
 #include "Scheduler/RefreshRateConfigs.h"
@@ -62,9 +62,11 @@
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncModulator.h"
 #include "SurfaceFlingerFactory.h"
-#include "SurfaceTracing.h"
 #include "TracedOrdinal.h"
+#include "Tracing/LayerTracing.h"
+#include "Tracing/TransactionTracing.h"
 #include "TransactionCallbackInvoker.h"
+#include "TransactionState.h"
 
 #include <atomic>
 #include <cstdint>
@@ -89,6 +91,7 @@
 
 class Client;
 class EventThread;
+class FlagManager;
 class FpsReporter;
 class TunnelModeEnabledReporter;
 class HdrLayerInfoReporter;
@@ -103,6 +106,7 @@
 class FrameTracer;
 class WindowInfosListenerInvoker;
 
+using gui::IRegionSamplingListener;
 using gui::ScreenCaptureResults;
 
 namespace frametimeline {
@@ -133,6 +137,8 @@
     eTransactionMask = 0x1f,
 };
 
+enum class LatchUnsignaledConfig { Always, Auto, Disabled };
+
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
 struct SurfaceFlingerBE {
@@ -154,29 +160,14 @@
     nsecs_t mFrameBuckets[NUM_BUCKETS] = {};
     nsecs_t mTotalTime = 0;
     std::atomic<nsecs_t> mLastSwapTime = 0;
-
-    // Double- vs. triple-buffering stats
-    struct BufferingStats {
-        size_t numSegments = 0;
-        nsecs_t totalTime = 0;
-
-        // "Two buffer" means that a third buffer was never used, whereas
-        // "double-buffered" means that on average the segment only used two
-        // buffers (though it may have used a third for some part of the
-        // segment)
-        nsecs_t twoBufferTime = 0;
-        nsecs_t doubleBufferedTime = 0;
-        nsecs_t tripleBufferedTime = 0;
-    };
-    mutable Mutex mBufferingStatsMutex;
-    std::unordered_map<std::string, BufferingStats> mBufferingStats;
 };
 
 class SurfaceFlinger : public BnSurfaceComposer,
                        public PriorityDumper,
                        private IBinder::DeathRecipient,
                        private HWC2::ComposerCallback,
-                       private ISchedulerCallback {
+                       private ICompositor,
+                       private scheduler::ISchedulerCallback {
 public:
     struct SkipInitializationTag {};
 
@@ -243,7 +234,7 @@
     static ui::Rotation internalDisplayOrientation;
 
     // Indicate if device wants color management on its display.
-    static bool useColorManagement;
+    static const constexpr bool useColorManagement = true;
 
     static bool useContextPriority;
 
@@ -259,18 +250,9 @@
     static ui::Dataspace wideColorGamutCompositionDataspace;
     static ui::PixelFormat wideColorGamutCompositionPixelFormat;
 
-    // Whether to use frame rate API when deciding about the refresh rate of the display. This
-    // variable is caches in SF, so that we can check it with each layer creation, and a void the
-    // overhead that is caused by reading from sysprop.
-    static bool useFrameRateApi;
-
     static constexpr SkipInitializationTag SkipInitialization;
 
-    // Whether or not SDR layers should be dimmed to the desired SDR white point instead of
-    // being treated as native display brightness
-    static bool enableSdrDimming;
-
-    static bool enableLatchUnsignaled;
+    static LatchUnsignaledConfig enableLatchUnsignaledConfig;
 
     // must be called before clients can connect
     void init() ANDROID_API;
@@ -281,12 +263,14 @@
     SurfaceFlingerBE& getBE() { return mBE; }
     const SurfaceFlingerBE& getBE() const { return mBE; }
 
-    // Schedule an asynchronous or synchronous task on the main thread.
-    template <typename F, typename T = std::invoke_result_t<F>>
-    [[nodiscard]] std::future<T> schedule(F&&);
-
-    // force full composition on all displays
-    void repaintEverything();
+    // Schedule commit of transactions on the main thread ahead of the next VSYNC.
+    void scheduleCommit(FrameHint);
+    // As above, but also force composite regardless if transactions were committed.
+    void scheduleComposite(FrameHint) override;
+    // As above, but also force dirty geometry to repaint.
+    void scheduleRepaint();
+    // Schedule sampling independently from commit or composite.
+    void scheduleSample();
 
     surfaceflinger::Factory& getFactory() { return mFactory; }
 
@@ -300,11 +284,6 @@
     // utility function to delete a texture on the main thread
     void deleteTextureAsync(uint32_t texture);
 
-    // called on the main thread by MessageQueue when an internal message
-    // is received
-    // TODO: this should be made accessible only to MessageQueue
-    void onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime);
-
     renderengine::RenderEngine& getRenderEngine() const;
 
     bool authenticateSurfaceTextureLocked(
@@ -312,6 +291,7 @@
 
     void onLayerFirstRef(Layer*);
     void onLayerDestroyed(Layer*);
+    void onLayerUpdate();
 
     void removeHierarchyFromOffscreenLayers(Layer* layer);
     void removeFromOffscreenLayers(Layer* layer);
@@ -343,14 +323,6 @@
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
 
-    virtual uint32_t setClientStateLocked(
-            const FrameTimelineInfo& info, const ComposerState& composerState,
-            int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime,
-            uint32_t permissions,
-            std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
-            REQUIRES(mStateLock);
-    virtual void commitTransactionLocked();
-
     virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
             REQUIRES(mStateLock);
 
@@ -360,6 +332,10 @@
         return static_cast<bool>(findDisplay(p));
     }
 
+    bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const {
+        return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize;
+    }
+
 private:
     friend class BufferLayer;
     friend class BufferQueueLayer;
@@ -371,14 +347,13 @@
     friend class MonitoredProducer;
     friend class RefreshRateOverlay;
     friend class RegionSamplingThread;
-    friend class SurfaceTracing;
+    friend class LayerTracing;
 
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class TransactionApplicationTest;
     friend class TunnelModeEnabledReporterTest;
 
-    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
     using VsyncModulator = scheduler::VsyncModulator;
     using TransactionSchedule = scheduler::TransactionSchedule;
     using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
@@ -471,96 +446,6 @@
         hal::Connection connection = hal::Connection::INVALID;
     };
 
-    class CountDownLatch {
-    public:
-        enum {
-            eSyncTransaction = 1 << 0,
-            eSyncInputWindows = 1 << 1,
-        };
-        explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
-
-        // True if there is no waiting condition after count down.
-        bool countDown(uint32_t flag) {
-            std::unique_lock<std::mutex> lock(mMutex);
-            if (mFlags == 0) {
-                return true;
-            }
-            mFlags &= ~flag;
-            if (mFlags == 0) {
-                mCountDownComplete.notify_all();
-                return true;
-            }
-            return false;
-        }
-
-        // Return true if triggered.
-        bool wait_until(const std::chrono::seconds& timeout) const {
-            std::unique_lock<std::mutex> lock(mMutex);
-            const auto untilTime = std::chrono::system_clock::now() + timeout;
-            while (mFlags != 0) {
-                // Conditional variables can be woken up sporadically, so we check count
-                // to verify the wakeup was triggered by |countDown|.
-                if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-    private:
-        uint32_t mFlags;
-        mutable std::condition_variable mCountDownComplete;
-        mutable std::mutex mMutex;
-    };
-
-    struct TransactionState {
-        TransactionState(const FrameTimelineInfo& frameTimelineInfo,
-                         const Vector<ComposerState>& composerStates,
-                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         const sp<IBinder>& applyToken,
-                         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-                         bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
-                         int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
-                         std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
-                         int originUid, uint64_t transactionId)
-              : frameTimelineInfo(frameTimelineInfo),
-                states(composerStates),
-                displays(displayStates),
-                flags(transactionFlags),
-                applyToken(applyToken),
-                inputWindowCommands(inputWindowCommands),
-                desiredPresentTime(desiredPresentTime),
-                isAutoTimestamp(isAutoTimestamp),
-                buffer(uncacheBuffer),
-                postTime(postTime),
-                permissions(permissions),
-                hasListenerCallbacks(hasListenerCallbacks),
-                listenerCallbacks(listenerCallbacks),
-                originPid(originPid),
-                originUid(originUid),
-                id(transactionId) {}
-
-        void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
-
-        FrameTimelineInfo frameTimelineInfo;
-        Vector<ComposerState> states;
-        Vector<DisplayState> displays;
-        uint32_t flags;
-        sp<IBinder> applyToken;
-        InputWindowCommands inputWindowCommands;
-        const int64_t desiredPresentTime;
-        const bool isAutoTimestamp;
-        client_cache_t buffer;
-        const int64_t postTime;
-        uint32_t permissions;
-        bool hasListenerCallbacks;
-        std::vector<ListenerCallbacks> listenerCallbacks;
-        int originPid;
-        int originUid;
-        uint64_t id;
-        std::shared_ptr<CountDownLatch> transactionCommittedSignal;
-    };
-
     template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
     static Dumper dumper(F&& dump) {
         using namespace std::placeholders;
@@ -631,12 +516,10 @@
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
             ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
-    status_t captureDisplay(const DisplayCaptureArgs& args,
-                            const sp<IScreenCaptureListener>& captureListener) override;
-    status_t captureDisplay(uint64_t displayOrLayerStack,
-                            const sp<IScreenCaptureListener>& captureListener) override;
-    status_t captureLayers(const LayerCaptureArgs& args,
-                           const sp<IScreenCaptureListener>& captureListener) override;
+
+    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override;
+    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override;
+    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override;
 
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
     status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
@@ -707,9 +590,10 @@
     status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
+    status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                         bool* outSupport) const override;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                           int8_t compatibility, int8_t changeFrameRateStrategy) override;
-    status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
 
     status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
                                   const FrameTimelineInfo& frameTimelineInfo) override;
@@ -729,9 +613,6 @@
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
-    // Implements RefBase.
-    void onFirstRef() override;
-
     // HWC2::ComposerCallback overrides:
     void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
                             std::optional<hal::VsyncPeriodNanos>) override;
@@ -741,16 +622,27 @@
                                                const hal::VsyncPeriodChangeTimeline&) override;
     void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
 
+    // ICompositor overrides:
+
+    // Commits transactions for layers and displays. Returns whether any state has been invalidated,
+    // i.e. whether a frame should be composited for each display.
+    bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) override;
+
+    // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
+    // via RenderEngine and the Composer HAL, respectively.
+    void composite(nsecs_t frameTime) override;
+
+    // Samples the composited frame via RegionSamplingThread.
+    void sample() override;
+
     /*
      * ISchedulerCallback
      */
 
     // Toggles hardware VSYNC by calling into HWC.
     void setVsyncEnabled(bool) override;
-    // Initiates a refresh rate change to be applied on invalidate.
-    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override;
-    // Forces full composition on all displays without resetting the scheduler idle timer.
-    void repaintEverythingForHWC() override;
+    // Initiates a refresh rate change to be applied on commit.
+    void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
     void kernelTimerChanged(bool expired) override;
     // Called when the frame rate override list changed to trigger an event.
@@ -763,26 +655,16 @@
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
 
-    /*
-     * Message handling
-     */
-    // Can only be called from the main thread or with mStateLock held
-    void signalTransaction();
-    // Can only be called from the main thread or with mStateLock held
-    void signalLayerUpdate();
-    void signalRefresh();
-
     // Called on the main thread in response to initializeDisplays()
     void onInitializeDisplays() REQUIRES(mStateLock);
     // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
     void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
-    status_t setActiveMode(const sp<IBinder>& displayToken, int id);
-    // Once HWC has returned the present fence, this sets the active mode and a new refresh
-    // rate in SF.
-    void setActiveModeInternal() REQUIRES(mStateLock);
+    status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
+    // Sets the active mode and a new refresh rate in SF.
+    void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
     // Calls to setActiveMode on the main thread if there is a pending mode change
     // that needs to be applied.
-    void performSetActiveMode() REQUIRES(mStateLock);
+    void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock);
     void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called when active mode is no longer is progress
     void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
@@ -790,31 +672,28 @@
     void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock);
 
+    // Returns true if the display has a visible HDR layer in its layer stack.
+    bool hasVisibleHdrLayer(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
+
     // Sets the desired display mode specs.
     status_t setDesiredDisplayModeSpecsInternal(
             const sp<DisplayDevice>& display,
             const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
             EXCLUDES(mStateLock);
 
-    // Handle the INVALIDATE message queue event, latching new buffers and applying
-    // incoming transactions
-    void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime);
+    void commitTransactions() EXCLUDES(mStateLock);
+    void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+    void doCommitTransactions() REQUIRES(mStateLock);
 
-    // Returns whether the transaction actually modified any state
-    bool handleMessageTransaction();
+    // Returns whether a new buffer has been latched.
+    bool latchBuffers();
 
-    // Handle the REFRESH message queue event, sending the current frame down to RenderEngine and
-    // the Composer HAL for presentation
-    void onMessageRefresh();
-
-    // Returns whether a new buffer has been latched (see handlePageFlip())
-    bool handleMessageInvalidate();
-
-    void handleTransaction(uint32_t transactionFlags);
-    void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+    void updateLayerGeometry();
 
     void updateInputFlinger();
-    void notifyWindowInfos();
+    void persistDisplayBrightness(bool needsComposite) REQUIRES(SF_MAIN_THREAD);
+    void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
+                          std::vector<gui::DisplayInfo>& outDisplayInfos);
     void commitInputWindowCommands() REQUIRES(mStateLock);
     void updateCursorAsync();
 
@@ -822,16 +701,11 @@
     void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
     void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
 
-    /* handlePageFlip - latch a new buffer if available and compute the dirty
-     * region. Returns whether a new buffer has been latched, i.e., whether it
-     * is necessary to perform a refresh during this vsync.
-     */
-    bool handlePageFlip();
 
     /*
      * Transactions
      */
-    void applyTransactionState(const FrameTimelineInfo& info, const Vector<ComposerState>& state,
+    bool applyTransactionState(const FrameTimelineInfo& info, const Vector<ComposerState>& state,
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -841,27 +715,44 @@
                                int originPid, int originUid, uint64_t transactionId)
             REQUIRES(mStateLock);
     // flush pending transaction that was presented after desiredPresentTime.
-    void flushTransactionQueues();
+    bool flushTransactionQueues(int64_t vsyncId);
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
-    uint32_t getTransactionFlags(uint32_t flags);
-    uint32_t peekTransactionFlags();
-    // Can only be called from the main thread or with mStateLock held
-    uint32_t setTransactionFlags(uint32_t flags);
+
+    uint32_t setClientStateLocked(const FrameTimelineInfo&, const ComposerState&,
+                                  int64_t desiredPresentTime, bool isAutoTimestamp,
+                                  int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
+
+    uint32_t getTransactionFlags() const;
+
+    // Sets the masked bits, and returns the old flags.
+    uint32_t setTransactionFlags(uint32_t mask);
+
+    // Clears and returns the masked bits.
+    uint32_t clearTransactionFlags(uint32_t mask);
+
     // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
     // where there are still pending transactions but we know they won't be ready until a frame
     // arrives from a different layer. So we need to ensure we performTransaction from invalidate
     // but there is no need to try and wake up immediately to do it. Rather we rely on
     // onFrameAvailable or another layer update to wake us up.
     void setTraversalNeeded();
-    uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule, const sp<IBinder>& = {});
-    void commitTransaction() REQUIRES(mStateLock);
+    uint32_t setTransactionFlags(uint32_t mask, TransactionSchedule,
+                                 const sp<IBinder>& applyToken = {});
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
             const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
-                    bufferLayersReadyToPresent) const REQUIRES(mStateLock);
+                    bufferLayersReadyToPresent,
+            bool allowLatchUnsignaled) const REQUIRES(mStateLock);
+    static LatchUnsignaledConfig getLatchUnsignaledConfig();
+    bool latchUnsignaledIsAllowed(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
+    bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock);
+    bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction)
+            REQUIRES(mStateLock);
+    bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId)
+            REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -869,35 +760,27 @@
     /*
      * Layer management
      */
-    status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
-                         PixelFormat format, uint32_t flags, LayerMetadata metadata,
-                         sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+    status_t createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
                          const sp<IBinder>& parentHandle, int32_t* outLayerId,
                          const sp<Layer>& parentLayer = nullptr,
                          uint32_t* outTransformHint = nullptr);
 
-    status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
-                                    uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                    PixelFormat& format, sp<IBinder>* outHandle,
-                                    sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
+    status_t createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
+                                    sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
+                                    sp<Layer>* outLayer);
 
-    status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
-                                    uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                    sp<IBinder>* outHandle, sp<Layer>* outLayer);
+    status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
+                                    sp<Layer>* outLayer);
 
-    status_t createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
-                               uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
+    status_t createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
                                sp<Layer>* outLayer);
 
-    status_t createContainerLayer(const sp<Client>& client, std::string name, uint32_t w,
-                                  uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                  sp<IBinder>* outHandle, sp<Layer>* outLayer);
+    status_t createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
+                                  sp<Layer>* outLayer);
 
-    status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+    status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
                          sp<IBinder>* outHandle, int32_t* outLayerId);
 
-    std::string getUniqueLayerName(const char* name);
-
     // called when all clients have released all their references to
     // this layer meaning it is entirely safe to destroy all
     // resources associated to this layer.
@@ -906,9 +789,8 @@
 
     // add a layer to SurfaceFlinger
     status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
-                            const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
-                            const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
-                            bool addToRoot, uint32_t* outTransformHint);
+                            const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot,
+                            uint32_t* outTransformHint);
 
     // Traverse through all the layers and compute and cache its bounds.
     void computeLayerBounds();
@@ -916,17 +798,17 @@
     // Boot animation, on/off animations and screen capture
     void startBootAnim();
 
-    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
-                                 ui::PixelFormat, bool allowProtected, bool grayscale,
-                                 const sp<IScreenCaptureListener>&);
-    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
-                                 const std::shared_ptr<renderengine::ExternalTexture>&,
-                                 bool regionSampling, bool grayscale,
-                                 const sp<IScreenCaptureListener>&);
-    status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
-                                    const std::shared_ptr<renderengine::ExternalTexture>&,
-                                    bool canCaptureBlackoutContent, bool regionSampling,
-                                    bool grayscale, ScreenCaptureResults&);
+    std::shared_future<renderengine::RenderEngineResult> captureScreenCommon(
+            RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize, ui::PixelFormat,
+            bool allowProtected, bool grayscale, const sp<IScreenCaptureListener>&);
+    std::shared_future<renderengine::RenderEngineResult> captureScreenCommon(
+            RenderAreaFuture, TraverseLayersFunction,
+            const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
+            bool grayscale, const sp<IScreenCaptureListener>&);
+    std::shared_future<renderengine::RenderEngineResult> renderScreenImplLocked(
+            const RenderArea&, TraverseLayersFunction,
+            const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
+            bool regionSampling, bool grayscale, ScreenCaptureResults&);
 
     // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
     // matching ownerUid
@@ -934,11 +816,7 @@
 
     void readPersistentProperties();
 
-    bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const {
-        return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize;
-    }
-
-    int getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
+    uint32_t getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
 
     /*
      * Display and layer stack management
@@ -1009,8 +887,6 @@
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
-    sp<DisplayDevice> getDisplayWithInputByLayer(Layer* layer) const REQUIRES(mStateLock);
-
     bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
         return display->getDisplayToken() == mActiveDisplayToken;
     }
@@ -1029,8 +905,6 @@
     /*
      * Compositing
      */
-    void invalidateHwcGeometry();
-
     void postComposition();
     void getCompositorTiming(CompositorTiming* compositorTiming);
     void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
@@ -1070,10 +944,6 @@
         getHwComposer().setVsyncEnabled(id, enabled);
     }
 
-    // Sets the refresh rate by switching active configs, if they are available for
-    // the desired refresh rate.
-    void changeRefreshRateLocked(const RefreshRate&, Scheduler::ModeEvent) REQUIRES(mStateLock);
-
     struct FenceWithFenceTime {
         sp<Fence> fence = Fence::NO_FENCE;
         std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
@@ -1119,15 +989,19 @@
         return {};
     }
 
-    // TODO(b/74619554): Remove special cases for primary display.
+    // TODO(b/182939859): SF conflates the primary (a.k.a. default) display with the first display
+    // connected at boot, which is typically internal. (Theoretically, it must be internal because
+    // SF does not support disconnecting it, though in practice HWC may circumvent this limitation.)
+    //
+    // SF inherits getInternalDisplayToken and getInternalDisplayId from ISurfaceComposer, so these
+    // locked counterparts are named consistently. Once SF supports headless mode and can designate
+    // any display as primary, the "internal" misnomer will be phased out.
     sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) {
-        const auto displayId = getInternalDisplayIdLocked();
-        return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
+        return getPhysicalDisplayTokenLocked(getInternalDisplayIdLocked());
     }
 
-    std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
-        const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
-        return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+    PhysicalDisplayId getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
+        return getHwComposer().getPrimaryDisplayId();
     }
 
     // Toggles use of HAL/GPU virtual displays.
@@ -1158,21 +1032,17 @@
     void dumpStaticScreenStats(std::string& result) const;
     // Not const because each Layer needs to query Fences and cache timestamps.
     void dumpFrameEventsLocked(std::string& result);
-
-    void recordBufferingStats(const std::string& layerName,
-                              std::vector<OccupancyTracker::Segment>&& history);
-    void dumpBufferingStats(std::string& result) const;
     void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
     void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
     void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
     LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
     void dumpOffscreenLayersProto(LayersProto& layersProto,
-                                  uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+                                  uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
     void dumpDisplayProto(LayersTraceProto& layersTraceProto) const;
 
     // Dumps state from HW Composer
     void dumpHwc(std::string& result) const;
-    LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
+    LayersProto dumpProtoFromMainThread(uint32_t traceFlags = LayerTracing::TRACE_ALL)
             EXCLUDES(mStateLock);
     void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
     void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
@@ -1185,8 +1055,6 @@
         return doDump(fd, args, asProto);
     }
 
-    void onFrameRateFlexibilityTokenReleased();
-
     static mat4 calculateColorMatrix(float saturation);
 
     void updateColorMatrixLocked();
@@ -1208,8 +1076,7 @@
     /*
      * Misc
      */
-    std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId)
-            REQUIRES(mStateLock);
+    std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock);
 
     static int calculateMaxAcquiredBufferCount(Fps refreshRate,
                                                std::chrono::nanoseconds presentLatency);
@@ -1220,7 +1087,7 @@
 
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
-
+    pid_t mPid;
     std::future<void> mRenderEnginePrimeCacheFuture;
 
     // access must be protected by mStateLock
@@ -1229,29 +1096,26 @@
     std::atomic<int32_t> mTransactionFlags = 0;
     std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
     bool mAnimTransactionPending = false;
+    std::atomic<uint32_t> mUniqueTransactionId = 1;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
-    bool mForceTraversal = false;
 
     // global color transform states
     Daltonizer mDaltonizer;
     float mGlobalSaturationFactor = 1.0f;
     mat4 mClientColorMatrix;
 
-    // Can't be unordered_set because wp<> isn't hashable
-    std::set<wp<IBinder>> mGraphicBufferProducerList;
     size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
     // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
     // this threshold, then begin logging.
     size_t mGraphicBufferProducerListSizeLogThreshold =
             static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
 
-    void removeGraphicBufferProducerAsync(const wp<IBinder>&);
-
     // protected by mStateLock (but we could use another lock)
     bool mLayersRemoved = false;
     bool mLayersAdded = false;
 
-    std::atomic<bool> mRepaintEverything = false;
+    std::atomic_bool mMustComposite = false;
+    std::atomic_bool mGeometryDirty = false;
 
     // constant members (no synchronization needed for access)
     const nsecs_t mBootTime = systemTime();
@@ -1278,7 +1142,6 @@
     bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
-    bool mGeometryInvalid = false;
     bool mAnimCompositionPending = false;
 
     // Tracks layers that have pending frames which are candidates for being
@@ -1313,20 +1176,21 @@
         std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
     } mVirtualDisplayIdGenerators;
 
-    // don't use a lock for these, we don't care
-    int mDebugRegion = 0;
-    bool mDebugDisableHWC = false;
-    bool mDebugDisableTransformHint = false;
+    std::atomic_uint mDebugFlashDelay = 0;
+    std::atomic_bool mDebugDisableHWC = false;
+    std::atomic_bool mDebugDisableTransformHint = false;
+    std::atomic<nsecs_t> mDebugInTransaction = 0;
+    std::atomic_bool mForceFullDamage = false;
+
     bool mLayerCachingEnabled = false;
-    volatile nsecs_t mDebugInTransaction = 0;
-    bool mForceFullDamage = false;
     bool mPropagateBackpressureClientComposition = false;
     sp<SurfaceInterceptor> mInterceptor;
 
-    SurfaceTracing mTracing{*this};
-    std::mutex mTracingLock;
-    bool mTracingEnabled = false;
-    bool mTracePostComposition = false;
+    LayerTracing mLayerTracing{*this};
+    bool mLayerTracingEnabled = false;
+
+    TransactionTracing mTransactionTracing;
+    bool mTransactionTracingEnabled = false;
     std::atomic<bool> mTracingEnabledChanged = false;
 
     const std::shared_ptr<TimeStats> mTimeStats;
@@ -1343,18 +1207,9 @@
 
     TransactionCallbackInvoker mTransactionCallbackInvoker;
 
-    // these are thread safe
-    std::unique_ptr<MessageQueue> mEventQueue;
+    // Thread-safe.
     FrameTracker mAnimFrameTracker;
 
-    // protected by mDestroyedLayerLock;
-    mutable Mutex mDestroyedLayerLock;
-    Vector<Layer const *> mDestroyedLayers;
-
-    nsecs_t mRefreshStartTime = 0;
-
-    std::atomic<bool> mRefreshPending = false;
-
     // We maintain a pool of pre-generated texture names to hand out to avoid
     // layer creation needing to run on the main thread (which it would
     // otherwise need to do to access RenderEngine).
@@ -1366,7 +1221,7 @@
     Condition mTransactionQueueCV;
     std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
             mPendingTransactionQueues GUARDED_BY(mQueueLock);
-    std::queue<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
+    std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
     /*
      * Feature prototyping
      */
@@ -1413,7 +1268,7 @@
     /*
      * Scheduler
      */
-    std::unique_ptr<Scheduler> mScheduler;
+    std::unique_ptr<scheduler::Scheduler> mScheduler;
     scheduler::ConnectionHandle mAppConnectionHandle;
     scheduler::ConnectionHandle mSfConnectionHandle;
 
@@ -1442,15 +1297,12 @@
     const float mInternalDisplayDensity;
     const float mEmulatedDisplayDensity;
 
-    sp<os::IInputFlinger> mInputFlinger;
     // Should only be accessed by the main thread.
+    sp<os::IInputFlinger> mInputFlinger;
     InputWindowCommands mInputWindowCommands;
 
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
-    // This should only be accessed on the main thread.
-    nsecs_t mFrameStartTime = 0;
-
     void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock);
 
     // Flag used to set override desired display mode from backdoor
@@ -1462,31 +1314,18 @@
     // be any issues with a raw pointer referencing an invalid object.
     std::unordered_set<Layer*> mOffscreenLayers;
 
-    int mFrameRateFlexibilityTokenCount = 0;
-
-    sp<IBinder> mDebugFrameRateFlexibilityToken;
-
     BufferCountTracker mBufferCountTracker;
 
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
     mutable Mutex mCreatedLayersLock;
     struct LayerCreatedState {
-        LayerCreatedState(const wp<Layer>& layer, const wp<IBinder>& parent,
-                          const wp<Layer> parentLayer, const wp<IBinder>& producer, bool addToRoot)
-              : layer(layer),
-                initialParent(parent),
-                initialParentLayer(parentLayer),
-                initialProducer(producer),
-                addToRoot(addToRoot) {}
+        LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
+              : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
         wp<Layer> layer;
         // Indicates the initial parent of the created layer, only used for creating layer in
         // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
-        wp<IBinder> initialParent;
-        wp<Layer> initialParentLayer;
-        // Indicates the initial graphic buffer producer of the created layer, only used for
-        // creating layer in SurfaceFlinger.
-        wp<IBinder> initialProducer;
+        wp<Layer> initialParent;
         // Indicates whether the layer getting created should be added at root if there's no parent
         // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
         // be added offscreen.
@@ -1497,16 +1336,12 @@
     // thread.
     std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
     void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
-                              const wp<IBinder>& parent, const wp<Layer> parentLayer,
-                              const wp<IBinder>& producer, bool addToRoot);
+                              const wp<Layer> parent, bool addToRoot);
     auto getLayerCreatedState(const sp<IBinder>& handle);
     sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
 
     std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
-    void scheduleRegionSamplingThread();
-    void notifyRegionSamplingThread();
-
     bool isRefreshRateOverlayEnabled() const REQUIRES(mStateLock) {
         return std::any_of(mDisplays.begin(), mDisplays.end(),
                            [](std::pair<wp<IBinder>, sp<DisplayDevice>> display) {
@@ -1517,6 +1352,20 @@
     wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
 
     const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
+
+    std::unique_ptr<FlagManager> mFlagManager;
+
+    // returns the framerate of the layer with the given sequence ID
+    float getLayerFramerate(nsecs_t now, int32_t id) const {
+        return mScheduler->getLayerFramerate(now, id);
+    }
+
+    struct {
+        bool sessionEnabled = false;
+        nsecs_t commitStart;
+        nsecs_t compositeStart;
+        nsecs_t presentEnd;
+    } mPowerHintSessionData GUARDED_BY(SF_MAIN_THREAD);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 89d1c4d..b81b445 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -38,7 +38,6 @@
 #include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/MessageQueue.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
 #include "Scheduler/VsyncController.h"
@@ -51,10 +50,6 @@
     return std::make_unique<android::impl::HWComposer>(serviceName);
 }
 
-std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() {
-    return std::make_unique<android::impl::MessageQueue>();
-}
-
 std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
         Fps currentRefreshRate) {
     if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
@@ -64,12 +59,6 @@
     }
 }
 
-std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
-        const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
-        ISchedulerCallback& callback) {
-    return std::make_unique<Scheduler>(std::move(refreshRateConfigs), callback);
-}
-
 sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
     return new android::impl::SurfaceInterceptor();
 }
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index b8bf2ba..501629d 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -27,11 +27,8 @@
     virtual ~DefaultFactory();
 
     std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
-    std::unique_ptr<MessageQueue> createMessageQueue() override;
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) override;
-    std::unique_ptr<Scheduler> createScheduler(
-            const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override;
     sp<SurfaceInterceptor> createSurfaceInterceptor() override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 13c95dd..6153e8e 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,41 +16,38 @@
 
 #pragma once
 
-#include "Fps.h"
-
-#include <cutils/compiler.h>
-#include <utils/StrongPointer.h>
-
 #include <cinttypes>
 #include <functional>
 #include <memory>
 #include <string>
 
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
+#include <scheduler/Fps.h>
+
 namespace android {
 
 typedef int32_t PixelFormat;
 
 class BufferQueueLayer;
-class BufferStateLayer;
 class BufferLayerConsumer;
-class EffectLayer;
+class BufferStateLayer;
 class ContainerLayer;
 class DisplayDevice;
+class EffectLayer;
 class FrameTracer;
 class GraphicBuffer;
 class HWComposer;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
 class Layer;
-class MessageQueue;
-class Scheduler;
 class StartPropertySetThread;
 class SurfaceFlinger;
 class SurfaceInterceptor;
 class TimeStats;
 
 struct DisplayDeviceCreationArgs;
-struct ISchedulerCallback;
 struct LayerCreationArgs;
 
 namespace compositionengine {
@@ -76,11 +73,8 @@
 class Factory {
 public:
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
-    virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
     virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) = 0;
-    virtual std::unique_ptr<Scheduler> createScheduler(
-            const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) = 0;
     virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index e15eae8..16f6e31 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -195,17 +195,6 @@
     return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0;
 }
 
-bool use_color_management(bool defaultValue) {
-    auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
-    auto tmpHasHDRDisplayVal = has_HDR_display(defaultValue);
-    auto tmpHasWideColorDisplayVal = has_wide_color_display(defaultValue);
-
-    auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement :
-        defaultValue;
-
-    return tmpuseColorManagementVal || tmpHasHDRDisplayVal || tmpHasWideColorDisplayVal;
-}
-
 int64_t default_composition_dataspace(Dataspace defaultValue) {
     auto temp = SurfaceFlingerProperties::default_composition_dataspace();
     if (temp.has_value()) {
@@ -315,14 +304,6 @@
     return defaultValue;
 }
 
-bool use_frame_rate_api(bool defaultValue) {
-    auto temp = SurfaceFlingerProperties::use_frame_rate_api();
-    if (temp.has_value()) {
-        return *temp;
-    }
-    return defaultValue;
-}
-
 bool enable_sdr_dimming(bool defaultValue) {
     return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
 }
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 039d316..8d0e426 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -59,8 +59,6 @@
 SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
         SurfaceFlingerProperties::primary_display_orientation_values defaultValue);
 
-bool use_color_management(bool defaultValue);
-
 int64_t default_composition_dataspace(
         android::hardware::graphics::common::V1_2::Dataspace defaultValue);
 
@@ -90,8 +88,6 @@
 
 bool support_kernel_idle_timer(bool defaultValue);
 
-bool use_frame_rate_api(bool defaultValue);
-
 int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
 
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 9be3abe..0782fef 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -323,11 +323,10 @@
 }
 
 void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
-        uint32_t layerStack)
-{
+                                             ui::LayerStack layerStack) {
     SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
     LayerStackChange* layerStackChange(change->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack);
+    layerStackChange->set_layer_stack(layerStack.id);
 }
 
 void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
@@ -568,12 +567,11 @@
     }
 }
 
-void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
-        int32_t sequenceId, uint32_t layerStack)
-{
+void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
+                                                    ui::LayerStack layerStack) {
     DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
     LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack);
+    layerStackChange->set_layer_stack(layerStack.id);
 }
 
 void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 7b331b9..970c3e5 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -160,7 +160,7 @@
     void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
             const Region& transRegion);
     void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
-    void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+    void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack);
     void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
     void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
     void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
@@ -183,8 +183,7 @@
     DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
     void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
             const sp<const IGraphicBufferProducer>& surface);
-    void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
-            uint32_t layerStack);
+    void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack);
     void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags);
     void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
             uint32_t h);
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
deleted file mode 100644
index 5963737..0000000
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "SurfaceTracing"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "SurfaceTracing.h"
-#include <SurfaceFlinger.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <utils/SystemClock.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
-
-bool SurfaceTracing::enable() {
-    std::scoped_lock lock(mTraceLock);
-    if (mEnabled) {
-        return false;
-    }
-
-    if (flagIsSet(TRACE_SYNC)) {
-        runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig);
-    } else {
-        runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig,
-                                                               mFlinger.mTracingLock);
-    }
-    mEnabled = true;
-    return true;
-}
-
-bool SurfaceTracing::disable() {
-    std::scoped_lock lock(mTraceLock);
-    if (!mEnabled) {
-        return false;
-    }
-    mEnabled = false;
-    runner->stop();
-    return true;
-}
-
-bool SurfaceTracing::isEnabled() const {
-    std::scoped_lock lock(mTraceLock);
-    return mEnabled;
-}
-
-status_t SurfaceTracing::writeToFile() {
-    std::scoped_lock lock(mTraceLock);
-    if (!mEnabled) {
-        return STATUS_OK;
-    }
-    return runner->writeToFile();
-}
-
-void SurfaceTracing::notify(const char* where) {
-    std::scoped_lock lock(mTraceLock);
-    if (mEnabled) {
-        runner->notify(where);
-    }
-}
-
-void SurfaceTracing::notifyLocked(const char* where) {
-    std::scoped_lock lock(mTraceLock);
-    if (mEnabled) {
-        runner->notifyLocked(where);
-    }
-}
-
-void SurfaceTracing::dump(std::string& result) const {
-    std::scoped_lock lock(mTraceLock);
-    base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
-    if (mEnabled) {
-        runner->dump(result);
-    }
-}
-
-void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
-    // use the swap trick to make sure memory is released
-    std::queue<LayersTraceProto>().swap(mStorage);
-    mSizeInBytes = newSize;
-    mUsedInBytes = 0U;
-}
-
-void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
-    size_t protoSize = static_cast<size_t>(proto.ByteSize());
-    while (mUsedInBytes + protoSize > mSizeInBytes) {
-        if (mStorage.empty()) {
-            return;
-        }
-        mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
-        mStorage.pop();
-    }
-    mUsedInBytes += protoSize;
-    mStorage.emplace();
-    mStorage.back().Swap(&proto);
-}
-
-void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
-    fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
-
-    while (!mStorage.empty()) {
-        auto entry = fileProto->add_entry();
-        entry->Swap(&mStorage.front());
-        mStorage.pop();
-    }
-}
-
-SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config)
-      : mFlinger(flinger), mConfig(config) {
-    mBuffer.setSize(mConfig.bufferSize);
-}
-
-void SurfaceTracing::Runner::notify(const char* where) {
-    LayersTraceProto entry = traceLayers(where);
-    mBuffer.emplace(std::move(entry));
-}
-
-status_t SurfaceTracing::Runner::stop() {
-    return writeToFile();
-}
-
-LayersTraceFileProto SurfaceTracing::createLayersTraceFileProto() {
-    LayersTraceFileProto fileProto;
-    fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
-                               LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
-    return fileProto;
-}
-
-status_t SurfaceTracing::Runner::writeToFile() {
-    ATRACE_CALL();
-
-    LayersTraceFileProto fileProto = createLayersTraceFileProto();
-    std::string output;
-
-    mBuffer.flush(&fileProto);
-    mBuffer.reset(mConfig.bufferSize);
-
-    if (!fileProto.SerializeToString(&output)) {
-        ALOGE("Could not save the proto file! Permission denied");
-        return PERMISSION_DENIED;
-    }
-
-    // -rw-r--r--
-    const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-    if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(),
-                                          true)) {
-        ALOGE("Could not save the proto file! There are missing fields");
-        return PERMISSION_DENIED;
-    }
-
-    return NO_ERROR;
-}
-
-LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) {
-    ATRACE_CALL();
-
-    LayersTraceProto entry;
-    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
-    entry.set_where(where);
-    LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags));
-
-    if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) {
-        mFlinger.dumpOffscreenLayersProto(layers);
-    }
-    entry.mutable_layers()->Swap(&layers);
-
-    if (flagIsSet(SurfaceTracing::TRACE_HWC)) {
-        std::string hwcDump;
-        mFlinger.dumpHwc(hwcDump);
-        entry.set_hwc_blob(hwcDump);
-    }
-    if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) {
-        entry.set_excludes_composition_state(true);
-    }
-    entry.set_missed_entries(mMissedTraceEntries);
-    mFlinger.dumpDisplayProto(entry);
-    return entry;
-}
-
-void SurfaceTracing::Runner::dump(std::string& result) const {
-    base::StringAppendF(&result, "  number of entries: %zu (%.2fMB / %.2fMB)\n",
-                        mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
-                        float(mBuffer.size()) / float(1_MB));
-}
-
-SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config,
-                                         std::mutex& sfLock)
-      : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) {
-    mEnabled = true;
-    mThread = std::thread(&AsyncRunner::loop, this);
-}
-
-void SurfaceTracing::AsyncRunner::loop() {
-    while (mEnabled) {
-        LayersTraceProto entry;
-        bool entryAdded = traceWhenNotified(&entry);
-        if (entryAdded) {
-            mBuffer.emplace(std::move(entry));
-        }
-        if (mWriteToFile) {
-            Runner::writeToFile();
-            mWriteToFile = false;
-        }
-    }
-}
-
-bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) {
-    std::unique_lock<std::mutex> lock(mSfLock);
-    mCanStartTrace.wait(lock);
-    if (!mAddEntry) {
-        return false;
-    }
-    *outProto = traceLayers(mWhere);
-    mAddEntry = false;
-    mMissedTraceEntries = 0;
-    return true;
-}
-
-void SurfaceTracing::AsyncRunner::notify(const char* where) {
-    std::scoped_lock lock(mSfLock);
-    notifyLocked(where);
-}
-
-void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) {
-    mWhere = where;
-    if (mAddEntry) {
-        mMissedTraceEntries++;
-    }
-    mAddEntry = true;
-    mCanStartTrace.notify_one();
-}
-
-status_t SurfaceTracing::AsyncRunner::writeToFile() {
-    mWriteToFile = true;
-    mCanStartTrace.notify_one();
-    return STATUS_OK;
-}
-
-status_t SurfaceTracing::AsyncRunner::stop() {
-    mEnabled = false;
-    mCanStartTrace.notify_one();
-    mThread.join();
-    return Runner::writeToFile();
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
deleted file mode 100644
index cea1a33..0000000
--- a/services/surfaceflinger/SurfaceTracing.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android-base/thread_annotations.h>
-#include <layerproto/LayerProtoHeader.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <thread>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-class SurfaceFlinger;
-constexpr auto operator""_MB(unsigned long long const num) {
-    return num * 1024 * 1024;
-}
-/*
- * SurfaceTracing records layer states during surface flinging. Manages tracing state and
- * configuration.
- */
-class SurfaceTracing {
-public:
-    SurfaceTracing(SurfaceFlinger& flinger);
-    bool enable();
-    bool disable();
-    status_t writeToFile();
-    bool isEnabled() const;
-    /*
-     * Adds a trace entry, must be called from the drawing thread or while holding the
-     * SurfaceFlinger tracing lock.
-     */
-    void notify(const char* where);
-    /*
-     * Adds a trace entry, called while holding the SurfaceFlinger tracing lock.
-     */
-    void notifyLocked(const char* where) /* REQUIRES(mSfLock) */;
-
-    void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; }
-    void dump(std::string& result) const;
-
-    enum : uint32_t {
-        TRACE_CRITICAL = 1 << 0,
-        TRACE_INPUT = 1 << 1,
-        TRACE_COMPOSITION = 1 << 2,
-        TRACE_EXTRA = 1 << 3,
-        TRACE_HWC = 1 << 4,
-        // Add non-geometry composition changes to the trace.
-        TRACE_BUFFERS = 1 << 5,
-        // Add entries from the drawing thread post composition.
-        TRACE_SYNC = 1 << 6,
-        TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
-    };
-    void setTraceFlags(uint32_t flags) { mConfig.flags = flags; }
-    bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
-    static LayersTraceFileProto createLayersTraceFileProto();
-
-private:
-    class Runner;
-    static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
-    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
-
-    SurfaceFlinger& mFlinger;
-    mutable std::mutex mTraceLock;
-    bool mEnabled GUARDED_BY(mTraceLock) = false;
-    std::unique_ptr<Runner> runner GUARDED_BY(mTraceLock);
-
-    struct Config {
-        uint32_t flags = TRACE_CRITICAL | TRACE_INPUT | TRACE_SYNC;
-        size_t bufferSize = DEFAULT_BUFFER_SIZE;
-    } mConfig;
-
-    /*
-     * ring buffer.
-     */
-    class LayersTraceBuffer {
-    public:
-        size_t size() const { return mSizeInBytes; }
-        size_t used() const { return mUsedInBytes; }
-        size_t frameCount() const { return mStorage.size(); }
-
-        void setSize(size_t newSize) { mSizeInBytes = newSize; }
-        void reset(size_t newSize);
-        void emplace(LayersTraceProto&& proto);
-        void flush(LayersTraceFileProto* fileProto);
-
-    private:
-        size_t mUsedInBytes = 0U;
-        size_t mSizeInBytes = DEFAULT_BUFFER_SIZE;
-        std::queue<LayersTraceProto> mStorage;
-    };
-
-    /*
-     * Implements a synchronous way of adding trace entries. This must be called
-     * from the drawing thread.
-     */
-    class Runner {
-    public:
-        Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config);
-        virtual ~Runner() = default;
-        virtual status_t stop();
-        virtual status_t writeToFile();
-        virtual void notify(const char* where);
-        /* Cannot be called with a synchronous runner. */
-        virtual void notifyLocked(const char* /* where */) {}
-        void dump(std::string& result) const;
-
-    protected:
-        bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
-        SurfaceFlinger& mFlinger;
-        SurfaceTracing::Config mConfig;
-        SurfaceTracing::LayersTraceBuffer mBuffer;
-        uint32_t mMissedTraceEntries = 0;
-        LayersTraceProto traceLayers(const char* where);
-    };
-
-    /*
-     * Implements asynchronous way to add trace entries called from a separate thread while holding
-     * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not
-     * scheduled in time.
-     */
-    class AsyncRunner : public Runner {
-    public:
-        AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock);
-        virtual ~AsyncRunner() = default;
-        status_t stop() override;
-        status_t writeToFile() override;
-        void notify(const char* where) override;
-        void notifyLocked(const char* where);
-
-    private:
-        std::mutex& mSfLock;
-        std::condition_variable mCanStartTrace;
-        std::thread mThread;
-        const char* mWhere = "";
-        bool mWriteToFile = false;
-        bool mEnabled = false;
-        bool mAddEntry = false;
-        void loop();
-        bool traceWhenNotified(LayersTraceProto* outProto);
-    };
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index bcc3e4e..4686eed 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -12,6 +12,9 @@
     srcs: [
         "TimeStats.cpp",
     ],
+    header_libs: [
+        "libscheduler_headers",
+    ],
     shared_libs: [
         "android.hardware.graphics.composer@2.4",
         "libbase",
@@ -24,6 +27,9 @@
         "libutils",
     ],
     export_include_dirs: ["."],
+    export_header_lib_headers: [
+        "libscheduler_headers",
+    ],
     export_shared_lib_headers: [
         "libtimestats_proto",
     ],
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 7c1f21f..b1a2bda 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <unordered_map>
 #undef LOG_TAG
 #define LOG_TAG "TimeStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,6 +27,7 @@
 
 #include <algorithm>
 #include <chrono>
+#include <unordered_map>
 
 #include "TimeStats.h"
 #include "timestatsproto/TimeStatsHelper.h"
@@ -58,15 +58,15 @@
     return histogramProto;
 }
 
-SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(int32_t gameMode) {
+SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(GameMode gameMode) {
     switch (gameMode) {
-        case TimeStatsHelper::GameModeUnsupported:
+        case GameMode::Unsupported:
             return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSUPPORTED;
-        case TimeStatsHelper::GameModeStandard:
+        case GameMode::Standard:
             return SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD;
-        case TimeStatsHelper::GameModePerformance:
+        case GameMode::Performance:
             return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
-        case TimeStatsHelper::GameModeBattery:
+        case GameMode::Battery:
             return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
         default:
             return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
@@ -454,7 +454,7 @@
 void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
                                                    std::optional<Fps> renderRate,
                                                    SetFrameRateVote frameRateVote,
-                                                   int32_t gameMode) {
+                                                   GameMode gameMode) {
     ATRACE_CALL();
     ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
 
@@ -554,7 +554,7 @@
 }
 
 bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName,
-                                         int32_t gameMode) {
+                                         GameMode gameMode) {
     uint32_t layerRecords = 0;
     for (const auto& record : mTimeStats.stats) {
         if (record.second.stats.count({uid, layerName, gameMode}) > 0) {
@@ -564,11 +564,11 @@
         layerRecords += record.second.stats.size();
     }
 
-    return mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+    return layerRecords < MAX_NUM_LAYER_STATS;
 }
 
 void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
-                            uid_t uid, nsecs_t postTime, int32_t gameMode) {
+                            uid_t uid, nsecs_t postTime, GameMode gameMode) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -718,7 +718,7 @@
 
 void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
                                Fps displayRefreshRate, std::optional<Fps> renderRate,
-                               SetFrameRateVote frameRateVote, int32_t gameMode) {
+                               SetFrameRateVote frameRateVote, GameMode gameMode) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -744,7 +744,7 @@
 void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence,
                                 Fps displayRefreshRate, std::optional<Fps> renderRate,
-                                SetFrameRateVote frameRateVote, int32_t gameMode) {
+                                SetFrameRateVote frameRateVote, GameMode gameMode) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -823,7 +823,7 @@
     // the first jank record is not dropped.
 
     static const std::string kDefaultLayerName = "none";
-    static constexpr int32_t kDefaultGameMode = TimeStatsHelper::GameModeUnsupported;
+    constexpr GameMode kDefaultGameMode = GameMode::Unsupported;
 
     const int32_t refreshRateBucket =
             clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 9e70684..77c7973 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,21 +17,22 @@
 #pragma once
 
 #include <cstdint>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <variant>
 
-#include <../Fps.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 #include <gui/JankInfo.h>
+#include <gui/LayerMetadata.h>
 #include <timestatsproto/TimeStatsHelper.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
 #include <ui/FenceTime.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
-#include <deque>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-#include <variant>
+#include <scheduler/Fps.h>
 
 using namespace android::surfaceflinger;
 
@@ -79,7 +80,7 @@
                                             const std::shared_ptr<FenceTime>& readyFence) = 0;
 
     virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
-                             uid_t uid, nsecs_t postTime, int32_t gameMode) = 0;
+                             uid_t uid, nsecs_t postTime, GameMode) = 0;
     virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
     // Reasons why latching a particular buffer may be skipped
     enum class LatchSkipReason {
@@ -101,11 +102,11 @@
     // rendering path, as they flush prior fences if those fences have fired.
     virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
                                 Fps displayRefreshRate, std::optional<Fps> renderRate,
-                                SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
+                                SetFrameRateVote frameRateVote, GameMode) = 0;
     virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence,
                                  Fps displayRefreshRate, std::optional<Fps> renderRate,
-                                 SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
+                                 SetFrameRateVote frameRateVote, GameMode) = 0;
 
     // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
     // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -123,19 +124,21 @@
         std::optional<Fps> renderRate;
         uid_t uid = 0;
         std::string layerName;
-        int32_t gameMode = 0;
+        GameMode gameMode = GameMode::Unsupported;
         int32_t reasons = 0;
         nsecs_t displayDeadlineDelta = 0;
         nsecs_t displayPresentJitter = 0;
         nsecs_t appDeadlineDelta = 0;
 
+        static bool isOptApproxEqual(std::optional<Fps> lhs, std::optional<Fps> rhs) {
+            return (!lhs && !rhs) || (lhs && rhs && isApproxEqual(*lhs, *rhs));
+        }
+
         bool operator==(const JankyFramesInfo& o) const {
-            return Fps::EqualsInBuckets{}(refreshRate, o.refreshRate) &&
-                    ((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
-                     (renderRate != std::nullopt && o.renderRate != std::nullopt &&
-                      Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
-                    uid == o.uid && layerName == o.layerName && gameMode == o.gameMode &&
-                    reasons == o.reasons && displayDeadlineDelta == o.displayDeadlineDelta &&
+            return isApproxEqual(refreshRate, o.refreshRate) &&
+                    isOptApproxEqual(renderRate, o.renderRate) && uid == o.uid &&
+                    layerName == o.layerName && gameMode == o.gameMode && reasons == o.reasons &&
+                    displayDeadlineDelta == o.displayDeadlineDelta &&
                     displayPresentJitter == o.displayPresentJitter &&
                     appDeadlineDelta == o.appDeadlineDelta;
         }
@@ -192,7 +195,7 @@
     struct LayerRecord {
         uid_t uid;
         std::string layerName;
-        int32_t gameMode = 0;
+        GameMode gameMode = GameMode::Unsupported;
         // This is the index in timeRecords, at which the timestamps for that
         // specific frame are still not fully received. This is not waiting for
         // fences to signal, but rather waiting to receive those fences/timestamps.
@@ -245,7 +248,7 @@
                                     const std::shared_ptr<FenceTime>& readyFence) override;
 
     void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
-                     nsecs_t postTime, int32_t gameMode) override;
+                     nsecs_t postTime, GameMode) override;
     void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
     void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
     void incrementBadDesiredPresent(int32_t layerId) override;
@@ -254,12 +257,11 @@
     void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence) override;
     void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                        Fps displayRefreshRate, std::optional<Fps> renderRate,
-                        SetFrameRateVote frameRateVote, int32_t gameMode) override;
+                        Fps displayRefreshRate, std::optional<Fps> renderRate, SetFrameRateVote,
+                        GameMode) override;
     void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
-                         std::optional<Fps> renderRate, SetFrameRateVote frameRateVote,
-                         int32_t gameMode) override;
+                         std::optional<Fps> renderRate, SetFrameRateVote, GameMode) override;
 
     void incrementJankyFrames(const JankyFramesInfo& info) override;
     // Clean up the layer record
@@ -280,11 +282,11 @@
     bool populateLayerAtom(std::string* pulledData);
     bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
-                                            std::optional<Fps> renderRate,
-                                            SetFrameRateVote frameRateVote, int32_t gameMode);
+                                            std::optional<Fps> renderRate, SetFrameRateVote,
+                                            GameMode);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
-    bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, int32_t gameMode);
+    bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, GameMode);
 
     void enable();
     void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index ffb2f09..69afa2a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -16,9 +16,10 @@
 #include "timestatsproto/TimeStatsHelper.h"
 
 #include <android-base/stringprintf.h>
-#include <inttypes.h>
+#include <ftl/enum.h>
 
 #include <array>
+#include <cinttypes>
 
 #define HISTOGRAM_SIZE 85
 
@@ -91,51 +92,15 @@
     return result;
 }
 
-std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
-    switch (compatibility) {
-        case FrameRateCompatibility::Undefined:
-            return "Undefined";
-        case FrameRateCompatibility::Default:
-            return "Default";
-        case FrameRateCompatibility::ExactOrMultiple:
-            return "ExactOrMultiple";
-    }
-}
-
-std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
-    switch (seamlessness) {
-        case Seamlessness::Undefined:
-            return "Undefined";
-        case Seamlessness::ShouldBeSeamless:
-            return "ShouldBeSeamless";
-        case Seamlessness::NotRequired:
-            return "NotRequired";
-    }
-}
-
 std::string TimeStatsHelper::SetFrameRateVote::toString() const {
     std::string result;
     StringAppendF(&result, "frameRate = %.2f\n", frameRate);
     StringAppendF(&result, "frameRateCompatibility = %s\n",
-                  toString(frameRateCompatibility).c_str());
-    StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+                  ftl::enum_string(frameRateCompatibility).c_str());
+    StringAppendF(&result, "seamlessness = %s\n", ftl::enum_string(seamlessness).c_str());
     return result;
 }
 
-std::string TimeStatsHelper::TimeStatsLayer::toString(int32_t gameMode) const {
-    switch (gameMode) {
-        case TimeStatsHelper::GameModeUnsupported:
-            return "GameModeUnsupported";
-        case TimeStatsHelper::GameModeStandard:
-            return "GameModeStandard";
-        case TimeStatsHelper::GameModePerformance:
-            return "GameModePerformance";
-        case TimeStatsHelper::GameModeBattery:
-            return "GameModeBattery";
-        default:
-            return "GameModeUnspecified";
-    }
-}
 std::string TimeStatsHelper::TimeStatsLayer::toString() const {
     std::string result = "\n";
     StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -143,7 +108,7 @@
     StringAppendF(&result, "uid = %d\n", uid);
     StringAppendF(&result, "layerName = %s\n", layerName.c_str());
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
-    StringAppendF(&result, "gameMode = %s\n", toString(gameMode).c_str());
+    StringAppendF(&result, "gameMode = %s\n", ftl::enum_string(gameMode).c_str());
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
     StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 2afff8d..438561c 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include <gui/LayerMetadata.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
 #include <utils/Timers.h>
 
@@ -63,6 +64,8 @@
             Undefined = 0,
             Default = 1,
             ExactOrMultiple = 2,
+
+            ftl_last = ExactOrMultiple
         } frameRateCompatibility = FrameRateCompatibility::Undefined;
 
         // Needs to be in sync with atoms.proto
@@ -70,25 +73,13 @@
             Undefined = 0,
             ShouldBeSeamless = 1,
             NotRequired = 2,
+
+            ftl_last = NotRequired
         } seamlessness = Seamlessness::Undefined;
 
-        static std::string toString(FrameRateCompatibility);
-        static std::string toString(Seamlessness);
         std::string toString() const;
     };
 
-    /**
-     * GameMode of the layer. GameModes are set by SysUI through WMShell.
-     * Actual game mode definitions are managed by GameManager.java
-     * The values defined here should always be in sync with the ones in GameManager.
-     */
-    enum GameMode {
-        GameModeUnsupported = 0,
-        GameModeStandard = 1,
-        GameModePerformance = 2,
-        GameModeBattery = 3,
-    };
-
     class TimeStatsLayer {
     public:
         uid_t uid;
@@ -96,7 +87,7 @@
         std::string packageName;
         int32_t displayRefreshRateBucket = 0;
         int32_t renderRateBucket = 0;
-        int32_t gameMode = 0;
+        GameMode gameMode = GameMode::Unsupported;
         int32_t totalFrames = 0;
         int32_t droppedFrames = 0;
         int32_t lateAcquireFrames = 0;
@@ -106,7 +97,6 @@
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
-        std::string toString(int32_t gameMode) const;
         SFTimeStatsLayerProto toProto() const;
     };
 
@@ -137,13 +127,14 @@
     struct LayerStatsKey {
         uid_t uid = 0;
         std::string layerName;
-        int32_t gameMode = 0;
+        GameMode gameMode = GameMode::Unsupported;
 
         struct Hasher {
             size_t operator()(const LayerStatsKey& key) const {
                 size_t uidHash = std::hash<uid_t>{}(key.uid);
                 size_t layerNameHash = std::hash<std::string>{}(key.layerName);
-                size_t gameModeHash = std::hash<int32_t>{}(key.gameMode);
+                using T = std::underlying_type_t<GameMode>;
+                size_t gameModeHash = std::hash<T>{}(static_cast<T>(key.gameMode));
                 return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash));
             }
         };
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
new file mode 100644
index 0000000..d136e0b
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerTracing"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <SurfaceFlinger.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+#include <utils/Trace.h>
+
+#include "LayerTracing.h"
+#include "RingBuffer.h"
+
+namespace android {
+
+LayerTracing::LayerTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {
+    mBuffer = std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>();
+}
+
+LayerTracing::~LayerTracing() = default;
+
+bool LayerTracing::enable() {
+    std::scoped_lock lock(mTraceLock);
+    if (mEnabled) {
+        return false;
+    }
+    mBuffer->setSize(mBufferSizeInBytes);
+    mEnabled = true;
+    return true;
+}
+
+bool LayerTracing::disable() {
+    std::scoped_lock lock(mTraceLock);
+    if (!mEnabled) {
+        return false;
+    }
+    mEnabled = false;
+    LayersTraceFileProto fileProto = createTraceFileProto();
+    mBuffer->writeToFile(fileProto, FILE_NAME);
+    mBuffer->reset();
+    return true;
+}
+
+bool LayerTracing::isEnabled() const {
+    std::scoped_lock lock(mTraceLock);
+    return mEnabled;
+}
+
+status_t LayerTracing::writeToFile() {
+    std::scoped_lock lock(mTraceLock);
+    if (!mEnabled) {
+        return STATUS_OK;
+    }
+    LayersTraceFileProto fileProto = createTraceFileProto();
+    return mBuffer->writeToFile(fileProto, FILE_NAME);
+}
+
+void LayerTracing::setTraceFlags(uint32_t flags) {
+    std::scoped_lock lock(mTraceLock);
+    mFlags = flags;
+}
+
+void LayerTracing::setBufferSize(size_t bufferSizeInBytes) {
+    std::scoped_lock lock(mTraceLock);
+    mBufferSizeInBytes = bufferSizeInBytes;
+}
+
+bool LayerTracing::flagIsSet(uint32_t flags) const {
+    return (mFlags & flags) == flags;
+}
+
+LayersTraceFileProto LayerTracing::createTraceFileProto() const {
+    LayersTraceFileProto fileProto;
+    fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
+                               LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+    return fileProto;
+}
+
+void LayerTracing::dump(std::string& result) const {
+    std::scoped_lock lock(mTraceLock);
+    base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+    mBuffer->dump(result);
+}
+
+void LayerTracing::notify(const char* where) {
+    ATRACE_CALL();
+    std::scoped_lock lock(mTraceLock);
+    if (!mEnabled) {
+        return;
+    }
+
+    ATRACE_CALL();
+    LayersTraceProto entry;
+    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+    entry.set_where(where);
+    LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
+
+    if (flagIsSet(LayerTracing::TRACE_EXTRA)) {
+        mFlinger.dumpOffscreenLayersProto(layers);
+    }
+    entry.mutable_layers()->Swap(&layers);
+
+    if (flagIsSet(LayerTracing::TRACE_HWC)) {
+        std::string hwcDump;
+        mFlinger.dumpHwc(hwcDump);
+        entry.set_hwc_blob(hwcDump);
+    }
+    if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
+        entry.set_excludes_composition_state(true);
+    }
+    mFlinger.dumpDisplayProto(entry);
+    mBuffer->emplace(std::move(entry));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
new file mode 100644
index 0000000..8ca3587
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <layerproto/LayerProtoHeader.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <memory>
+#include <mutex>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer;
+
+class SurfaceFlinger;
+
+/*
+ * LayerTracing records layer states during surface flinging. Manages tracing state and
+ * configuration.
+ */
+class LayerTracing {
+public:
+    LayerTracing(SurfaceFlinger& flinger);
+    ~LayerTracing();
+    bool enable();
+    bool disable();
+    bool isEnabled() const;
+    status_t writeToFile();
+    LayersTraceFileProto createTraceFileProto() const;
+    void notify(const char* where);
+
+    enum : uint32_t {
+        TRACE_INPUT = 1 << 1,
+        TRACE_COMPOSITION = 1 << 2,
+        TRACE_EXTRA = 1 << 3,
+        TRACE_HWC = 1 << 4,
+        TRACE_BUFFERS = 1 << 5,
+        TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
+    };
+    void setTraceFlags(uint32_t flags);
+    bool flagIsSet(uint32_t flags) const;
+    void setBufferSize(size_t bufferSizeInBytes);
+    void dump(std::string&) const;
+
+private:
+    static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
+
+    SurfaceFlinger& mFlinger;
+    uint32_t mFlags = TRACE_INPUT;
+    mutable std::mutex mTraceLock;
+    bool mEnabled GUARDED_BY(mTraceLock) = false;
+    std::unique_ptr<RingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer
+            GUARDED_BY(mTraceLock);
+    size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = 20 * 1024 * 1024;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
new file mode 100644
index 0000000..3b2626d
--- /dev/null
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <queue>
+
+namespace android {
+
+class SurfaceFlinger;
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer {
+public:
+    size_t size() const { return mSizeInBytes; }
+    size_t used() const { return mUsedInBytes; }
+    size_t frameCount() const { return mStorage.size(); }
+    void setSize(size_t newSize) { mSizeInBytes = newSize; }
+    const std::string& front() const { return mStorage.front(); }
+    const std::string& back() const { return mStorage.back(); }
+
+    void reset() {
+        // use the swap trick to make sure memory is released
+        std::deque<std::string>().swap(mStorage);
+        mUsedInBytes = 0U;
+    }
+
+    void writeToProto(FileProto& fileProto) {
+        fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) +
+                                           fileProto.entry().size());
+        for (const std::string& entry : mStorage) {
+            EntryProto* entryProto = fileProto.add_entry();
+            entryProto->ParseFromString(entry);
+        }
+    }
+
+    status_t writeToFile(FileProto& fileProto, std::string filename) {
+        ATRACE_CALL();
+        writeToProto(fileProto);
+        std::string output;
+        if (!fileProto.SerializeToString(&output)) {
+            ALOGE("Could not serialize proto.");
+            return UNKNOWN_ERROR;
+        }
+
+        // -rw-r--r--
+        const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+        if (!android::base::WriteStringToFile(output, filename, mode, getuid(), getgid(), true)) {
+            ALOGE("Could not save the proto file.");
+            return PERMISSION_DENIED;
+        }
+        return NO_ERROR;
+    }
+
+    std::vector<std::string> emplace(std::string&& serializedProto) {
+        std::vector<std::string> replacedEntries;
+        size_t protoSize = static_cast<size_t>(serializedProto.size());
+        while (mUsedInBytes + protoSize > mSizeInBytes) {
+            if (mStorage.empty()) {
+                return {};
+            }
+            mUsedInBytes -= static_cast<size_t>(mStorage.front().size());
+            replacedEntries.emplace_back(mStorage.front());
+            mStorage.pop_front();
+        }
+        mUsedInBytes += protoSize;
+        mStorage.emplace_back(serializedProto);
+        return replacedEntries;
+    }
+
+    std::vector<std::string> emplace(EntryProto&& proto) {
+        std::string serializedProto;
+        proto.SerializeToString(&serializedProto);
+        return emplace(std::move(serializedProto));
+    }
+
+    void dump(std::string& result) const {
+        std::chrono::milliseconds duration(0);
+        if (frameCount() > 0) {
+            EntryProto entry;
+            entry.ParseFromString(mStorage.front());
+            duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+                    std::chrono::nanoseconds(systemTime() - entry.elapsed_realtime_nanos()));
+        }
+        const int64_t durationCount = duration.count();
+        base::StringAppendF(&result,
+                            "  number of entries: %zu (%.2fMB / %.2fMB) duration: %" PRIi64 "ms\n",
+                            frameCount(), float(used()) / (1024.f * 1024.f),
+                            float(size()) / (1024.f * 1024.f), durationCount);
+    }
+
+private:
+    size_t mUsedInBytes = 0U;
+    size_t mSizeInBytes = 0U;
+    std::deque<std::string> mStorage;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
new file mode 100644
index 0000000..378deb0
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/SurfaceComposerClient.h>
+#include <ui/Rect.h>
+
+#include "LayerProtoHelper.h"
+#include "TransactionProtoParser.h"
+
+namespace android::surfaceflinger {
+
+proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t,
+                                                        LayerHandleToIdFn getLayerId,
+                                                        DisplayHandleToIdFn getDisplayId) {
+    proto::TransactionState proto;
+    proto.set_pid(t.originPid);
+    proto.set_uid(t.originUid);
+    proto.set_vsync_id(t.frameTimelineInfo.vsyncId);
+    proto.set_input_event_id(t.frameTimelineInfo.inputEventId);
+    proto.set_post_time(t.postTime);
+
+    for (auto& layerState : t.states) {
+        proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state, getLayerId)));
+    }
+
+    for (auto& displayState : t.displays) {
+        proto.mutable_display_changes()->Add(std::move(toProto(displayState, getDisplayId)));
+    }
+    return proto;
+}
+
+proto::TransactionState TransactionProtoParser::toProto(
+        const std::map<int32_t /* layerId */, TracingLayerState>& states) {
+    proto::TransactionState proto;
+    for (auto& [layerId, state] : states) {
+        proto::LayerState layerProto = toProto(state, nullptr);
+        if (layerProto.has_buffer_data()) {
+            proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data();
+            bufferProto->set_buffer_id(state.bufferId);
+            bufferProto->set_width(state.bufferWidth);
+            bufferProto->set_height(state.bufferHeight);
+        }
+        layerProto.set_has_sideband_stream(state.hasSidebandStream);
+        layerProto.set_layer_id(state.layerId);
+        layerProto.set_parent_id(state.parentId);
+        layerProto.set_relative_parent_id(state.relativeParentId);
+        if (layerProto.has_window_info_handle()) {
+            layerProto.mutable_window_info_handle()->set_crop_layer_id(state.inputCropId);
+        }
+        proto.mutable_layer_changes()->Add(std::move(layerProto));
+    }
+    return proto;
+}
+
+proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer,
+                                                  LayerHandleToIdFn getLayerId) {
+    proto::LayerState proto;
+    if (getLayerId != nullptr) {
+        proto.set_layer_id(getLayerId(layer.surface));
+    } else {
+        proto.set_layer_id(layer.layerId);
+    }
+
+    proto.set_what(layer.what);
+
+    if (layer.what & layer_state_t::ePositionChanged) {
+        proto.set_x(layer.x);
+        proto.set_y(layer.y);
+    }
+    if (layer.what & layer_state_t::eLayerChanged) {
+        proto.set_z(layer.z);
+    }
+    if (layer.what & layer_state_t::eSizeChanged) {
+        proto.set_w(layer.w);
+        proto.set_h(layer.h);
+    }
+    if (layer.what & layer_state_t::eLayerStackChanged) {
+        proto.set_layer_stack(layer.layerStack.id);
+    }
+    if (layer.what & layer_state_t::eFlagsChanged) {
+        proto.set_flags(layer.flags);
+        proto.set_mask(layer.mask);
+    }
+    if (layer.what & layer_state_t::eMatrixChanged) {
+        proto::LayerState_Matrix22* matrixProto = proto.mutable_matrix();
+        matrixProto->set_dsdx(layer.matrix.dsdx);
+        matrixProto->set_dsdy(layer.matrix.dsdy);
+        matrixProto->set_dtdx(layer.matrix.dtdx);
+        matrixProto->set_dtdy(layer.matrix.dtdy);
+    }
+    if (layer.what & layer_state_t::eCornerRadiusChanged) {
+        proto.set_corner_radius(layer.cornerRadius);
+    }
+    if (layer.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        proto.set_background_blur_radius(layer.backgroundBlurRadius);
+    }
+
+    if (layer.what & layer_state_t::eAlphaChanged) {
+        proto.set_alpha(layer.alpha);
+    }
+
+    if (layer.what & layer_state_t::eColorChanged) {
+        proto::LayerState_Color3* colorProto = proto.mutable_color();
+        colorProto->set_r(layer.color.r);
+        colorProto->set_g(layer.color.g);
+        colorProto->set_b(layer.color.b);
+    }
+    if (layer.what & layer_state_t::eTransparentRegionChanged) {
+        LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
+    }
+    if (layer.what & layer_state_t::eTransformChanged) {
+        proto.set_transform(layer.transform);
+    }
+    if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
+        proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
+    }
+    if (layer.what & layer_state_t::eCropChanged) {
+        LayerProtoHelper::writeToProto(layer.crop, proto.mutable_crop());
+    }
+    if (layer.what & layer_state_t::eBufferChanged) {
+        proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
+        if (layer.bufferData.buffer) {
+            bufferProto->set_buffer_id(layer.bufferData.buffer->getId());
+            bufferProto->set_width(layer.bufferData.buffer->getWidth());
+            bufferProto->set_height(layer.bufferData.buffer->getHeight());
+        }
+        bufferProto->set_frame_number(layer.bufferData.frameNumber);
+        bufferProto->set_flags(layer.bufferData.flags.get());
+        bufferProto->set_cached_buffer_id(layer.bufferData.cachedBuffer.id);
+    }
+    if (layer.what & layer_state_t::eSidebandStreamChanged) {
+        proto.set_has_sideband_stream(layer.sidebandStream != nullptr);
+    }
+
+    if (layer.what & layer_state_t::eApiChanged) {
+        proto.set_api(layer.api);
+    }
+
+    if (layer.what & layer_state_t::eColorTransformChanged) {
+        LayerProtoHelper::writeToProto(layer.colorTransform, proto.mutable_color_transform());
+    }
+    if (layer.what & layer_state_t::eBlurRegionsChanged) {
+        for (auto& region : layer.blurRegions) {
+            LayerProtoHelper::writeToProto(region, proto.add_blur_regions());
+        }
+    }
+
+    if ((layer.what & layer_state_t::eReparent) && getLayerId != nullptr) {
+        int32_t layerId = layer.parentSurfaceControlForChild
+                ? getLayerId(layer.parentSurfaceControlForChild->getHandle())
+                : -1;
+        proto.set_parent_id(layerId);
+    }
+    if ((layer.what & layer_state_t::eRelativeLayerChanged) && getLayerId != nullptr) {
+        int32_t layerId = layer.relativeLayerSurfaceControl
+                ? getLayerId(layer.relativeLayerSurfaceControl->getHandle())
+                : -1;
+        proto.set_relative_parent_id(layerId);
+    }
+
+    if (layer.what & layer_state_t::eInputInfoChanged) {
+        if (layer.windowInfoHandle) {
+            const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo();
+            proto::LayerState_WindowInfo* windowInfoProto = proto.mutable_window_info_handle();
+            windowInfoProto->set_layout_params_flags(inputInfo->flags.get());
+            windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->type));
+            LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
+                                           windowInfoProto->mutable_touchable_region());
+            windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
+            windowInfoProto->set_focusable(inputInfo->focusable);
+            windowInfoProto->set_has_wallpaper(inputInfo->hasWallpaper);
+            windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
+            proto::LayerState_Transform* transformProto = windowInfoProto->mutable_transform();
+            transformProto->set_dsdx(inputInfo->transform.dsdx());
+            transformProto->set_dtdx(inputInfo->transform.dtdx());
+            transformProto->set_dtdy(inputInfo->transform.dtdy());
+            transformProto->set_dsdy(inputInfo->transform.dsdy());
+            transformProto->set_tx(inputInfo->transform.tx());
+            transformProto->set_ty(inputInfo->transform.ty());
+            windowInfoProto->set_replace_touchable_region_with_crop(
+                    inputInfo->replaceTouchableRegionWithCrop);
+            if (getLayerId != nullptr) {
+                windowInfoProto->set_crop_layer_id(
+                        getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+            } else {
+                windowInfoProto->set_crop_layer_id(-1);
+            }
+        }
+    }
+    if (layer.what & layer_state_t::eBackgroundColorChanged) {
+        proto.set_bg_color_alpha(layer.bgColorAlpha);
+        proto.set_bg_color_dataspace(static_cast<int32_t>(layer.bgColorDataspace));
+        proto::LayerState_Color3* colorProto = proto.mutable_color();
+        colorProto->set_r(layer.color.r);
+        colorProto->set_g(layer.color.g);
+        colorProto->set_b(layer.color.b);
+    }
+    if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
+        proto.set_color_space_agnostic(layer.colorSpaceAgnostic);
+    }
+    if (layer.what & layer_state_t::eShadowRadiusChanged) {
+        proto.set_shadow_radius(layer.shadowRadius);
+    }
+    if (layer.what & layer_state_t::eFrameRateSelectionPriority) {
+        proto.set_frame_rate_selection_priority(layer.frameRateSelectionPriority);
+    }
+    if (layer.what & layer_state_t::eFrameRateChanged) {
+        proto.set_frame_rate(layer.frameRate);
+        proto.set_frame_rate_compatibility(layer.frameRateCompatibility);
+        proto.set_change_frame_rate_strategy(layer.changeFrameRateStrategy);
+    }
+    if (layer.what & layer_state_t::eFixedTransformHintChanged) {
+        proto.set_fixed_transform_hint(layer.fixedTransformHint);
+    }
+    if (layer.what & layer_state_t::eAutoRefreshChanged) {
+        proto.set_auto_refresh(layer.autoRefresh);
+    }
+    if (layer.what & layer_state_t::eTrustedOverlayChanged) {
+        proto.set_is_trusted_overlay(layer.isTrustedOverlay);
+    }
+    if (layer.what & layer_state_t::eBufferCropChanged) {
+        LayerProtoHelper::writeToProto(layer.bufferCrop, proto.mutable_buffer_crop());
+    }
+    if (layer.what & layer_state_t::eDestinationFrameChanged) {
+        LayerProtoHelper::writeToProto(layer.destinationFrame, proto.mutable_destination_frame());
+    }
+    if (layer.what & layer_state_t::eDropInputModeChanged) {
+        proto.set_drop_input_mode(
+                static_cast<proto::LayerState_DropInputMode>(layer.dropInputMode));
+    }
+    return proto;
+}
+
+proto::DisplayState TransactionProtoParser::toProto(const DisplayState& display,
+                                                    DisplayHandleToIdFn getDisplayId) {
+    proto::DisplayState proto;
+    proto.set_what(display.what);
+    if (getDisplayId != nullptr) {
+        proto.set_id(getDisplayId(display.token));
+    }
+
+    if (display.what & DisplayState::eLayerStackChanged) {
+        proto.set_layer_stack(display.layerStack.id);
+    }
+    if (display.what & DisplayState::eDisplayProjectionChanged) {
+        proto.set_orientation(static_cast<uint32_t>(display.orientation));
+        LayerProtoHelper::writeToProto(display.orientedDisplaySpaceRect,
+                                       proto.mutable_oriented_display_space_rect());
+        LayerProtoHelper::writeToProto(display.layerStackSpaceRect,
+                                       proto.mutable_layer_stack_space_rect());
+    }
+    if (display.what & DisplayState::eDisplaySizeChanged) {
+        proto.set_width(display.width);
+        proto.set_height(display.height);
+    }
+    if (display.what & DisplayState::eFlagsChanged) {
+        proto.set_flags(display.flags);
+    }
+    return proto;
+}
+
+proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) {
+    proto::LayerCreationArgs proto;
+    proto.set_layer_id(args.layerId);
+    proto.set_name(args.name);
+    proto.set_flags(args.flags);
+    proto.set_parent_id(args.parentId);
+    proto.set_mirror_from_id(args.mirrorFromId);
+    return proto;
+}
+
+TransactionState TransactionProtoParser::fromProto(const proto::TransactionState& proto,
+                                                   LayerIdToHandleFn getLayerHandle,
+                                                   DisplayIdToHandleFn getDisplayHandle) {
+    TransactionState t;
+    t.originPid = proto.pid();
+    t.originUid = proto.uid();
+    t.frameTimelineInfo.vsyncId = proto.vsync_id();
+    t.frameTimelineInfo.inputEventId = proto.input_event_id();
+    t.postTime = proto.post_time();
+    int32_t layerCount = proto.layer_changes_size();
+    t.states.reserve(static_cast<size_t>(layerCount));
+    for (int i = 0; i < layerCount; i++) {
+        ComposerState s;
+        fromProto(proto.layer_changes(i), getLayerHandle, s.state);
+        t.states.add(s);
+    }
+
+    int32_t displayCount = proto.display_changes_size();
+    t.displays.reserve(static_cast<size_t>(displayCount));
+    for (int i = 0; i < displayCount; i++) {
+        t.displays.add(fromProto(proto.display_changes(i), getDisplayHandle));
+    }
+    return t;
+}
+
+void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto,
+                                       TracingLayerCreationArgs& outArgs) {
+    outArgs.layerId = proto.layer_id();
+    outArgs.name = proto.name();
+    outArgs.flags = proto.flags();
+    outArgs.parentId = proto.parent_id();
+    outArgs.mirrorFromId = proto.mirror_from_id();
+}
+
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+                                       LayerIdToHandleFn getLayerHandle,
+                                       TracingLayerState& outState) {
+    fromProto(proto, getLayerHandle, static_cast<layer_state_t&>(outState));
+    if (proto.what() & layer_state_t::eReparent) {
+        outState.parentId = proto.parent_id();
+        outState.args.parentId = outState.parentId;
+    }
+    if (proto.what() & layer_state_t::eRelativeLayerChanged) {
+        outState.relativeParentId = proto.relative_parent_id();
+    }
+    if (proto.what() & layer_state_t::eInputInfoChanged) {
+        outState.inputCropId = proto.window_info_handle().crop_layer_id();
+    }
+    if (proto.what() & layer_state_t::eBufferChanged) {
+        const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
+        outState.bufferId = bufferProto.buffer_id();
+        outState.bufferWidth = bufferProto.width();
+        outState.bufferHeight = bufferProto.height();
+    }
+    if (proto.what() & layer_state_t::eSidebandStreamChanged) {
+        outState.hasSidebandStream = proto.has_sideband_stream();
+    }
+}
+
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+                                       LayerIdToHandleFn getLayerHandle, layer_state_t& layer) {
+    layer.layerId = proto.layer_id();
+    layer.what |= proto.what();
+
+    if (getLayerHandle != nullptr) {
+        layer.surface = getLayerHandle(layer.layerId);
+    }
+
+    if (proto.what() & layer_state_t::ePositionChanged) {
+        layer.x = proto.x();
+        layer.y = proto.y();
+    }
+    if (proto.what() & layer_state_t::eLayerChanged) {
+        layer.z = proto.z();
+    }
+    if (proto.what() & layer_state_t::eSizeChanged) {
+        layer.w = proto.w();
+        layer.h = proto.h();
+    }
+    if (proto.what() & layer_state_t::eLayerStackChanged) {
+        layer.layerStack.id = proto.layer_stack();
+    }
+    if (proto.what() & layer_state_t::eFlagsChanged) {
+        layer.flags = proto.flags();
+        layer.mask = proto.mask();
+    }
+    if (proto.what() & layer_state_t::eMatrixChanged) {
+        const proto::LayerState_Matrix22& matrixProto = proto.matrix();
+        layer.matrix.dsdx = matrixProto.dsdx();
+        layer.matrix.dsdy = matrixProto.dsdy();
+        layer.matrix.dtdx = matrixProto.dtdx();
+        layer.matrix.dtdy = matrixProto.dtdy();
+    }
+    if (proto.what() & layer_state_t::eCornerRadiusChanged) {
+        layer.cornerRadius = proto.corner_radius();
+    }
+    if (proto.what() & layer_state_t::eBackgroundBlurRadiusChanged) {
+        layer.backgroundBlurRadius = proto.background_blur_radius();
+    }
+
+    if (proto.what() & layer_state_t::eAlphaChanged) {
+        layer.alpha = proto.alpha();
+    }
+
+    if (proto.what() & layer_state_t::eColorChanged) {
+        const proto::LayerState_Color3& colorProto = proto.color();
+        layer.color.r = colorProto.r();
+        layer.color.g = colorProto.g();
+        layer.color.b = colorProto.b();
+    }
+    if (proto.what() & layer_state_t::eTransparentRegionChanged) {
+        LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
+    }
+    if (proto.what() & layer_state_t::eTransformChanged) {
+        layer.transform = proto.transform();
+    }
+    if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) {
+        layer.transformToDisplayInverse = proto.transform_to_display_inverse();
+    }
+    if (proto.what() & layer_state_t::eCropChanged) {
+        LayerProtoHelper::readFromProto(proto.crop(), layer.crop);
+    }
+    if (proto.what() & layer_state_t::eBufferChanged) {
+        const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
+        layer.bufferData.frameNumber = bufferProto.frame_number();
+        layer.bufferData.flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
+        layer.bufferData.cachedBuffer.id = bufferProto.cached_buffer_id();
+    }
+
+    if (proto.what() & layer_state_t::eApiChanged) {
+        layer.api = proto.api();
+    }
+
+    if (proto.what() & layer_state_t::eColorTransformChanged) {
+        LayerProtoHelper::readFromProto(proto.color_transform(), layer.colorTransform);
+    }
+    if (proto.what() & layer_state_t::eBlurRegionsChanged) {
+        layer.blurRegions.reserve(static_cast<size_t>(proto.blur_regions_size()));
+        for (int i = 0; i < proto.blur_regions_size(); i++) {
+            android::BlurRegion region;
+            LayerProtoHelper::readFromProto(proto.blur_regions(i), region);
+            layer.blurRegions.push_back(region);
+        }
+    }
+
+    if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) {
+        int32_t layerId = proto.parent_id();
+        layer.parentSurfaceControlForChild =
+                new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+                                   nullptr, layerId);
+    }
+    if ((proto.what() & layer_state_t::eRelativeLayerChanged) && (getLayerHandle != nullptr)) {
+        int32_t layerId = proto.relative_parent_id();
+        layer.relativeLayerSurfaceControl =
+                new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+                                   nullptr, layerId);
+    }
+
+    if ((proto.what() & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) {
+        gui::WindowInfo inputInfo;
+        const proto::LayerState_WindowInfo& windowInfoProto = proto.window_info_handle();
+
+        inputInfo.flags = static_cast<gui::WindowInfo::Flag>(windowInfoProto.layout_params_flags());
+        inputInfo.type = static_cast<gui::WindowInfo::Type>(windowInfoProto.layout_params_type());
+        LayerProtoHelper::readFromProto(windowInfoProto.touchable_region(),
+                                        inputInfo.touchableRegion);
+        inputInfo.surfaceInset = windowInfoProto.surface_inset();
+        inputInfo.focusable = windowInfoProto.focusable();
+        inputInfo.hasWallpaper = windowInfoProto.has_wallpaper();
+        inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
+        const proto::LayerState_Transform& transformProto = windowInfoProto.transform();
+        inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
+                                transformProto.dsdy());
+        inputInfo.transform.set(transformProto.tx(), transformProto.ty());
+        inputInfo.replaceTouchableRegionWithCrop =
+                windowInfoProto.replace_touchable_region_with_crop();
+        int32_t layerId = windowInfoProto.crop_layer_id();
+        if (getLayerHandle != nullptr) {
+            inputInfo.touchableRegionCropHandle = getLayerHandle(layerId);
+        }
+        layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
+    }
+    if (proto.what() & layer_state_t::eBackgroundColorChanged) {
+        layer.bgColorAlpha = proto.bg_color_alpha();
+        layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace());
+        const proto::LayerState_Color3& colorProto = proto.color();
+        layer.color.r = colorProto.r();
+        layer.color.g = colorProto.g();
+        layer.color.b = colorProto.b();
+    }
+    if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) {
+        layer.colorSpaceAgnostic = proto.color_space_agnostic();
+    }
+    if (proto.what() & layer_state_t::eShadowRadiusChanged) {
+        layer.shadowRadius = proto.shadow_radius();
+    }
+    if (proto.what() & layer_state_t::eFrameRateSelectionPriority) {
+        layer.frameRateSelectionPriority = proto.frame_rate_selection_priority();
+    }
+    if (proto.what() & layer_state_t::eFrameRateChanged) {
+        layer.frameRate = proto.frame_rate();
+        layer.frameRateCompatibility = static_cast<int8_t>(proto.frame_rate_compatibility());
+        layer.changeFrameRateStrategy = static_cast<int8_t>(proto.change_frame_rate_strategy());
+    }
+    if (proto.what() & layer_state_t::eFixedTransformHintChanged) {
+        layer.fixedTransformHint =
+                static_cast<ui::Transform::RotationFlags>(proto.fixed_transform_hint());
+    }
+    if (proto.what() & layer_state_t::eAutoRefreshChanged) {
+        layer.autoRefresh = proto.auto_refresh();
+    }
+    if (proto.what() & layer_state_t::eTrustedOverlayChanged) {
+        layer.isTrustedOverlay = proto.is_trusted_overlay();
+    }
+    if (proto.what() & layer_state_t::eBufferCropChanged) {
+        LayerProtoHelper::readFromProto(proto.buffer_crop(), layer.bufferCrop);
+    }
+    if (proto.what() & layer_state_t::eDestinationFrameChanged) {
+        LayerProtoHelper::readFromProto(proto.destination_frame(), layer.destinationFrame);
+    }
+    if (proto.what() & layer_state_t::eDropInputModeChanged) {
+        layer.dropInputMode = static_cast<gui::DropInputMode>(proto.drop_input_mode());
+    }
+}
+
+DisplayState TransactionProtoParser::fromProto(const proto::DisplayState& proto,
+                                               DisplayIdToHandleFn getDisplayHandle) {
+    DisplayState display;
+    display.what = proto.what();
+    if (getDisplayHandle != nullptr) {
+        display.token = getDisplayHandle(proto.id());
+    }
+
+    if (display.what & DisplayState::eLayerStackChanged) {
+        display.layerStack.id = proto.layer_stack();
+    }
+    if (display.what & DisplayState::eDisplayProjectionChanged) {
+        display.orientation = static_cast<ui::Rotation>(proto.orientation());
+        LayerProtoHelper::readFromProto(proto.oriented_display_space_rect(),
+                                        display.orientedDisplaySpaceRect);
+        LayerProtoHelper::readFromProto(proto.layer_stack_space_rect(),
+                                        display.layerStackSpaceRect);
+    }
+    if (display.what & DisplayState::eDisplaySizeChanged) {
+        display.width = proto.width();
+        display.height = proto.height();
+    }
+    if (display.what & DisplayState::eFlagsChanged) {
+        display.flags = proto.flags();
+    }
+    return display;
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
new file mode 100644
index 0000000..b78d3d9
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <layerproto/TransactionProto.h>
+#include <utils/RefBase.h>
+
+#include "TransactionState.h"
+
+namespace android::surfaceflinger {
+
+struct TracingLayerCreationArgs {
+    int32_t layerId;
+    std::string name;
+    uint32_t flags = 0;
+    int32_t parentId = -1;
+    int32_t mirrorFromId = -1;
+};
+
+struct TracingLayerState : layer_state_t {
+    uint64_t bufferId;
+    uint32_t bufferHeight;
+    uint32_t bufferWidth;
+    bool hasSidebandStream;
+    int32_t parentId;
+    int32_t relativeParentId;
+    int32_t inputCropId;
+    TracingLayerCreationArgs args;
+};
+
+class TransactionProtoParser {
+public:
+    typedef std::function<sp<IBinder>(int32_t)> LayerIdToHandleFn;
+    typedef std::function<sp<IBinder>(int32_t)> DisplayIdToHandleFn;
+    typedef std::function<int32_t(const sp<IBinder>&)> LayerHandleToIdFn;
+    typedef std::function<int32_t(const sp<IBinder>&)> DisplayHandleToIdFn;
+
+    static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn,
+                                           DisplayHandleToIdFn getDisplayIdFn);
+    static proto::TransactionState toProto(
+            const std::map<int32_t /* layerId */, TracingLayerState>&);
+
+    static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
+
+    static TransactionState fromProto(const proto::TransactionState&,
+                                      LayerIdToHandleFn getLayerHandleFn,
+                                      DisplayIdToHandleFn getDisplayHandleFn);
+    static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
+                          TracingLayerState& outState);
+    static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+
+private:
+    static proto::LayerState toProto(const layer_state_t&, LayerHandleToIdFn getLayerId);
+    static proto::DisplayState toProto(const DisplayState&, DisplayHandleToIdFn getDisplayId);
+    static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandle,
+                          layer_state_t& out);
+    static DisplayState fromProto(const proto::DisplayState&, DisplayIdToHandleFn getDisplayHandle);
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
new file mode 100644
index 0000000..b5966d5
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "TransactionTracing"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+#include <utils/Trace.h>
+
+#include "RingBuffer.h"
+#include "TransactionTracing.h"
+
+namespace android {
+
+TransactionTracing::TransactionTracing() {
+    mBuffer = std::make_unique<
+            RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>();
+}
+
+TransactionTracing::~TransactionTracing() = default;
+
+bool TransactionTracing::enable() {
+    std::scoped_lock lock(mTraceLock);
+    if (mEnabled) {
+        return false;
+    }
+    mBuffer->setSize(mBufferSizeInBytes);
+    mStartingTimestamp = systemTime();
+    mEnabled = true;
+    {
+        std::scoped_lock lock(mMainThreadLock);
+        mDone = false;
+        mThread = std::thread(&TransactionTracing::loop, this);
+    }
+    return true;
+}
+
+bool TransactionTracing::disable() {
+    std::thread thread;
+    {
+        std::scoped_lock lock(mMainThreadLock);
+        mDone = true;
+        mTransactionsAvailableCv.notify_all();
+        thread = std::move(mThread);
+    }
+    if (thread.joinable()) {
+        thread.join();
+    }
+
+    std::scoped_lock lock(mTraceLock);
+    if (!mEnabled) {
+        return false;
+    }
+    mEnabled = false;
+
+    writeToFileLocked();
+    mBuffer->reset();
+    mQueuedTransactions.clear();
+    mStartingStates.clear();
+    mLayerHandles.clear();
+    return true;
+}
+
+bool TransactionTracing::isEnabled() const {
+    std::scoped_lock lock(mTraceLock);
+    return mEnabled;
+}
+
+status_t TransactionTracing::writeToFile() {
+    std::scoped_lock lock(mTraceLock);
+    if (!mEnabled) {
+        return STATUS_OK;
+    }
+    return writeToFileLocked();
+}
+
+status_t TransactionTracing::writeToFileLocked() {
+    proto::TransactionTraceFile fileProto = createTraceFileProto();
+    addStartingStateToProtoLocked(fileProto);
+    return mBuffer->writeToFile(fileProto, FILE_NAME);
+}
+
+void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
+    std::scoped_lock lock(mTraceLock);
+    mBufferSizeInBytes = bufferSizeInBytes;
+    mBuffer->setSize(mBufferSizeInBytes);
+}
+
+proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
+    proto::TransactionTraceFile proto;
+    proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
+                           proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
+    return proto;
+}
+
+void TransactionTracing::dump(std::string& result) const {
+    std::scoped_lock lock(mTraceLock);
+    base::StringAppendF(&result, "Transaction tracing state: %s\n",
+                        mEnabled ? "enabled" : "disabled");
+    base::StringAppendF(&result,
+                        "  queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
+                        mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
+                        mStartingStates.size());
+    mBuffer->dump(result);
+}
+
+void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
+    std::scoped_lock lock(mTraceLock);
+    ATRACE_CALL();
+    if (!mEnabled) {
+        return;
+    }
+    mQueuedTransactions[transaction.id] =
+            TransactionProtoParser::toProto(transaction,
+                                            std::bind(&TransactionTracing::getLayerIdLocked, this,
+                                                      std::placeholders::_1),
+                                            nullptr);
+}
+
+void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
+                                                  int64_t vsyncId) {
+    CommittedTransactions committedTransactions;
+    committedTransactions.vsyncId = vsyncId;
+    committedTransactions.timestamp = systemTime();
+    committedTransactions.transactionIds.reserve(transactions.size());
+    for (const auto& transaction : transactions) {
+        committedTransactions.transactionIds.emplace_back(transaction.id);
+    }
+
+    mPendingTransactions.emplace_back(committedTransactions);
+    tryPushToTracingThread();
+}
+
+void TransactionTracing::loop() {
+    while (true) {
+        std::vector<CommittedTransactions> committedTransactions;
+        std::vector<int32_t> removedLayers;
+        {
+            std::unique_lock<std::mutex> lock(mMainThreadLock);
+            base::ScopedLockAssertion assumeLocked(mMainThreadLock);
+            mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
+                return mDone || !mCommittedTransactions.empty();
+            });
+            if (mDone) {
+                mCommittedTransactions.clear();
+                mRemovedLayers.clear();
+                break;
+            }
+
+            removedLayers = std::move(mRemovedLayers);
+            mRemovedLayers.clear();
+            committedTransactions = std::move(mCommittedTransactions);
+            mCommittedTransactions.clear();
+        } // unlock mMainThreadLock
+
+        addEntry(committedTransactions, removedLayers);
+    }
+}
+
+void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+                                  const std::vector<int32_t>& removedLayers) {
+    ATRACE_CALL();
+    std::scoped_lock lock(mTraceLock);
+    std::vector<std::string> removedEntries;
+    proto::TransactionTraceEntry entryProto;
+    for (const CommittedTransactions& entry : committedTransactions) {
+        entryProto.set_elapsed_realtime_nanos(entry.timestamp);
+        entryProto.set_vsync_id(entry.vsyncId);
+        entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size()));
+        for (auto& newLayer : mCreatedLayers) {
+            entryProto.mutable_added_layers()->Add(std::move(newLayer));
+        }
+        entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
+        for (auto& removedLayer : removedLayers) {
+            entryProto.mutable_removed_layers()->Add(removedLayer);
+        }
+        mCreatedLayers.clear();
+        entryProto.mutable_transactions()->Reserve(
+                static_cast<int32_t>(entry.transactionIds.size()));
+        for (const uint64_t& id : entry.transactionIds) {
+            auto it = mQueuedTransactions.find(id);
+            if (it != mQueuedTransactions.end()) {
+                entryProto.mutable_transactions()->Add(std::move(it->second));
+                mQueuedTransactions.erase(it);
+            } else {
+                ALOGW("Could not find transaction id %" PRIu64, id);
+            }
+        }
+
+        std::string serializedProto;
+        entryProto.SerializeToString(&serializedProto);
+        entryProto.Clear();
+        std::vector<std::string> entries = mBuffer->emplace(std::move(serializedProto));
+        removedEntries.reserve(removedEntries.size() + entries.size());
+        removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
+                              std::make_move_iterator(entries.end()));
+    }
+
+    proto::TransactionTraceEntry removedEntryProto;
+    for (const std::string& removedEntry : removedEntries) {
+        removedEntryProto.ParseFromString(removedEntry);
+        updateStartingStateLocked(removedEntryProto);
+        removedEntryProto.Clear();
+    }
+    mTransactionsAddedToBufferCv.notify_one();
+}
+
+void TransactionTracing::flush(int64_t vsyncId) {
+    while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+        tryPushToTracingThread();
+    }
+    std::unique_lock<std::mutex> lock(mTraceLock);
+    base::ScopedLockAssertion assumeLocked(mTraceLock);
+    mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
+        proto::TransactionTraceEntry entry;
+        if (mBuffer->used() > 0) {
+            entry.ParseFromString(mBuffer->back());
+        }
+        return mBuffer->used() > 0 && entry.vsync_id() >= vsyncId;
+    });
+}
+
+void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+                                      uint32_t flags, int parentId) {
+    std::scoped_lock lock(mTraceLock);
+    TracingLayerCreationArgs args{layerId, name, flags, parentId, -1 /* mirrorFromId */};
+    if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
+        ALOGW("Duplicate handles found. %p", layerHandle);
+    }
+    mLayerHandles[layerHandle] = layerId;
+    proto::LayerCreationArgs protoArgs = TransactionProtoParser::toProto(args);
+    proto::LayerCreationArgs protoArgsCopy = protoArgs;
+    mCreatedLayers.push_back(protoArgs);
+}
+
+void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
+                                            const std::string& name, int mirrorFromId) {
+    std::scoped_lock lock(mTraceLock);
+    TracingLayerCreationArgs args{layerId, name, 0 /* flags */, -1 /* parentId */, mirrorFromId};
+    if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
+        ALOGW("Duplicate handles found. %p", layerHandle);
+    }
+    mLayerHandles[layerHandle] = layerId;
+    mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
+}
+
+void TransactionTracing::onLayerRemoved(int32_t layerId) {
+    mPendingRemovedLayers.emplace_back(layerId);
+    tryPushToTracingThread();
+}
+
+void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
+    std::scoped_lock lock(mTraceLock);
+    mLayerHandles.erase(layerHandle);
+}
+
+void TransactionTracing::tryPushToTracingThread() {
+    // Try to acquire the lock from main thread.
+    if (mMainThreadLock.try_lock()) {
+        // We got the lock! Collect any pending transactions and continue.
+        mCommittedTransactions.insert(mCommittedTransactions.end(),
+                                      std::make_move_iterator(mPendingTransactions.begin()),
+                                      std::make_move_iterator(mPendingTransactions.end()));
+        mPendingTransactions.clear();
+        mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
+                              mPendingRemovedLayers.end());
+        mPendingRemovedLayers.clear();
+        mTransactionsAvailableCv.notify_one();
+        mMainThreadLock.unlock();
+    } else {
+        ALOGV("Couldn't get lock");
+    }
+}
+
+int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) {
+    if (layerHandle == nullptr) {
+        return -1;
+    }
+    auto it = mLayerHandles.find(layerHandle->localBinder());
+    if (it == mLayerHandles.end()) {
+        ALOGW("Could not find layer handle %p", layerHandle->localBinder());
+        return -1;
+    }
+    return it->second;
+}
+
+void TransactionTracing::updateStartingStateLocked(
+        const proto::TransactionTraceEntry& removedEntry) {
+    // Keep track of layer starting state so we can reconstruct the layer state as we purge
+    // transactions from the buffer.
+    for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
+        TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
+        startingState.layerId = addedLayer.layer_id();
+        TransactionProtoParser::fromProto(addedLayer, startingState.args);
+    }
+
+    // Merge layer states to starting transaction state.
+    for (const proto::TransactionState& transaction : removedEntry.transactions()) {
+        for (const proto::LayerState& layerState : transaction.layer_changes()) {
+            auto it = mStartingStates.find(layerState.layer_id());
+            if (it == mStartingStates.end()) {
+                ALOGW("Could not find layer id %d", layerState.layer_id());
+                continue;
+            }
+            TransactionProtoParser::fromProto(layerState, nullptr, it->second);
+        }
+    }
+
+    // Clean up stale starting states since the layer has been removed and the buffer does not
+    // contain any references to the layer.
+    for (const int32_t removedLayerId : removedEntry.removed_layers()) {
+        mStartingStates.erase(removedLayerId);
+    }
+}
+
+void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
+    proto::TransactionTraceEntry* entryProto = proto.add_entry();
+    entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
+    entryProto->set_vsync_id(0);
+    if (mStartingStates.size() == 0) {
+        return;
+    }
+
+    entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
+    for (auto& [layerId, state] : mStartingStates) {
+        entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(state.args));
+    }
+
+    proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates);
+    transactionProto.set_vsync_id(0);
+    transactionProto.set_post_time(mStartingTimestamp);
+    entryProto->mutable_transactions()->Add(std::move(transactionProto));
+}
+
+proto::TransactionTraceFile TransactionTracing::writeToProto() {
+    std::scoped_lock<std::mutex> lock(mTraceLock);
+    proto::TransactionTraceFile proto = createTraceFileProto();
+    addStartingStateToProtoLocked(proto);
+    mBuffer->writeToProto(proto);
+    return proto;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
new file mode 100644
index 0000000..26a3758
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <layerproto/TransactionProto.h>
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "TransactionProtoParser.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer;
+
+class SurfaceFlinger;
+class TransactionTracingTest;
+/*
+ * Records all committed transactions into a ring bufffer.
+ *
+ * Transactions come in via the binder thread. They are serialized to proto
+ * and stored in a map using the transaction id as key. Main thread will
+ * pass the list of transaction ids that are committed every vsync and notify
+ * the tracing thread. The tracing thread will then wake up and add the
+ * committed transactions to the ring buffer.
+ *
+ * When generating SF dump state, we will flush the buffer to a file which
+ * will then be included in the bugreport.
+ *
+ */
+class TransactionTracing {
+public:
+    TransactionTracing();
+    ~TransactionTracing();
+
+    bool enable();
+    bool disable();
+    bool isEnabled() const;
+
+    void addQueuedTransaction(const TransactionState&);
+    void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
+    status_t writeToFile();
+    void setBufferSize(size_t bufferSizeInBytes);
+    void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
+                      int parentId);
+    void onMirrorLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+                            int mirrorFromId);
+    void onLayerRemoved(int layerId);
+    void onHandleRemoved(BBinder* layerHandle);
+    void dump(std::string&) const;
+    static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
+    static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
+
+private:
+    friend class TransactionTracingTest;
+
+    static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
+
+    mutable std::mutex mTraceLock;
+    bool mEnabled GUARDED_BY(mTraceLock) = false;
+    std::unique_ptr<RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>> mBuffer
+            GUARDED_BY(mTraceLock);
+    size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
+    std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
+            GUARDED_BY(mTraceLock);
+    nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
+    std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
+    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+            GUARDED_BY(mTraceLock);
+    std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
+
+    // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
+    // otherwise will push data to temporary container.
+    std::mutex mMainThreadLock;
+    std::thread mThread GUARDED_BY(mMainThreadLock);
+    bool mDone GUARDED_BY(mMainThreadLock) = false;
+    std::condition_variable mTransactionsAvailableCv;
+    std::condition_variable mTransactionsAddedToBufferCv;
+    struct CommittedTransactions {
+        std::vector<uint64_t> transactionIds;
+        int64_t vsyncId;
+        int64_t timestamp;
+    };
+    std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
+    std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
+
+    std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
+    std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+
+    proto::TransactionTraceFile createTraceFileProto() const;
+    void loop();
+    void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+                  const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+    int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
+    void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
+    void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
+    void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
+    status_t writeToFileLocked() REQUIRES(mTraceLock);
+
+    // TEST
+    // Wait until all the committed transactions for the specified vsync id are added to the buffer.
+    void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
+    // Return buffer contents as trace file proto
+    proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 6af69f0..b705d9c 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -24,6 +24,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "TransactionCallbackInvoker.h"
+#include "BackgroundExecutor.h"
 
 #include <cinttypes>
 
@@ -49,121 +50,25 @@
     return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
 }
 
-TransactionCallbackInvoker::~TransactionCallbackInvoker() {
-    {
-        std::lock_guard lock(mMutex);
-        for (const auto& [listener, transactionStats] : mCompletedTransactions) {
-            listener->unlinkToDeath(mDeathRecipient);
-        }
-    }
-}
-
-status_t TransactionCallbackInvoker::startRegistration(const ListenerCallbacks& listenerCallbacks) {
-    std::lock_guard lock(mMutex);
-
-    auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
+void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) {
     auto& [listener, callbackIds] = listenerCallbacks;
-
-    if (inserted) {
-        if (mCompletedTransactions.count(listener) == 0) {
-            status_t err = listener->linkToDeath(mDeathRecipient);
-            if (err != NO_ERROR) {
-                ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
-                return err;
-            }
-        }
-        auto& transactionStatsDeque = mCompletedTransactions[listener];
-        transactionStatsDeque.emplace_back(callbackIds);
-    }
-
-    return NO_ERROR;
+    auto& transactionStatsDeque = mCompletedTransactions[listener];
+    transactionStatsDeque.emplace_back(callbackIds);
 }
 
-status_t TransactionCallbackInvoker::endRegistration(const ListenerCallbacks& listenerCallbacks) {
-    std::lock_guard lock(mMutex);
-
-    auto itr = mRegisteringTransactions.find(listenerCallbacks);
-    if (itr == mRegisteringTransactions.end()) {
-        ALOGE("cannot end a registration that does not exist");
-        return BAD_VALUE;
-    }
-
-    mRegisteringTransactions.erase(itr);
-
-    return NO_ERROR;
-}
-
-bool TransactionCallbackInvoker::isRegisteringTransaction(
-        const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
-    ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
-
-    auto itr = mRegisteringTransactions.find(listenerCallbacks);
-    return itr != mRegisteringTransactions.end();
-}
-
-status_t TransactionCallbackInvoker::registerPendingCallbackHandle(
-        const sp<CallbackHandle>& handle) {
-    std::lock_guard lock(mMutex);
-
-    // If we can't find the transaction stats something has gone wrong. The client should call
-    // startRegistration before trying to register a pending callback handle.
-    TransactionStats* transactionStats;
-    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
-    if (err != NO_ERROR) {
-        ALOGE("cannot find transaction stats");
-        return err;
-    }
-
-    mPendingTransactions[handle->listener][handle->callbackIds]++;
-    return NO_ERROR;
-}
-
-status_t TransactionCallbackInvoker::finalizeCallbackHandle(const sp<CallbackHandle>& handle,
-                                                            const std::vector<JankData>& jankData) {
-    auto listener = mPendingTransactions.find(handle->listener);
-    if (listener != mPendingTransactions.end()) {
-        auto& pendingCallbacks = listener->second;
-        auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
-        if (pendingCallback != pendingCallbacks.end()) {
-            auto& pendingCount = pendingCallback->second;
-
-            // Decrease the pending count for this listener
-            if (--pendingCount == 0) {
-                pendingCallbacks.erase(pendingCallback);
-            }
-        } else {
-            ALOGW("there are more latched callbacks than there were registered callbacks");
-        }
-        if (listener->second.size() == 0) {
-            mPendingTransactions.erase(listener);
-        }
-    } else {
-        ALOGW("cannot find listener in mPendingTransactions");
-    }
-
-    status_t err = addCallbackHandle(handle, jankData);
-    if (err != NO_ERROR) {
-        ALOGE("could not add callback handle");
-        return err;
-    }
-    return NO_ERROR;
-}
-
-status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles(
+status_t TransactionCallbackInvoker::addOnCommitCallbackHandles(
         const std::deque<sp<CallbackHandle>>& handles,
         std::deque<sp<CallbackHandle>>& outRemainingHandles) {
     if (handles.empty()) {
         return NO_ERROR;
     }
-    std::lock_guard lock(mMutex);
     const std::vector<JankData>& jankData = std::vector<JankData>();
     for (const auto& handle : handles) {
         if (!containsOnCommitCallbacks(handle->callbackIds)) {
             outRemainingHandles.push_back(handle);
             continue;
         }
-        status_t err = finalizeCallbackHandle(handle, jankData);
+        status_t err = addCallbackHandle(handle, jankData);
         if (err != NO_ERROR) {
             return err;
         }
@@ -172,14 +77,13 @@
     return NO_ERROR;
 }
 
-status_t TransactionCallbackInvoker::finalizePendingCallbackHandles(
+status_t TransactionCallbackInvoker::addCallbackHandles(
         const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
     if (handles.empty()) {
         return NO_ERROR;
     }
-    std::lock_guard lock(mMutex);
     for (const auto& handle : handles) {
-        status_t err = finalizeCallbackHandle(handle, jankData);
+        status_t err = addCallbackHandle(handle, jankData);
         if (err != NO_ERROR) {
             return err;
         }
@@ -190,12 +94,10 @@
 
 status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
         const sp<CallbackHandle>& handle) {
-    std::lock_guard lock(mMutex);
-
     return addCallbackHandle(handle, std::vector<JankData>());
 }
 
-status_t TransactionCallbackInvoker::findTransactionStats(
+status_t TransactionCallbackInvoker::findOrCreateTransactionStats(
         const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
         TransactionStats** outTransactionStats) {
     auto& transactionStatsDeque = mCompletedTransactions[listener];
@@ -208,9 +110,8 @@
             return NO_ERROR;
         }
     }
-
-    ALOGE("could not find transaction stats");
-    return BAD_VALUE;
+    *outTransactionStats = &transactionStatsDeque.emplace_back(callbackIds);
+    return NO_ERROR;
 }
 
 status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle,
@@ -218,7 +119,8 @@
     // If we can't find the transaction stats something has gone wrong. The client should call
     // startRegistration before trying to add a callback handle.
     TransactionStats* transactionStats;
-    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+    status_t err =
+            findOrCreateTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
     if (err != NO_ERROR) {
         return err;
     }
@@ -229,6 +131,38 @@
     // destroyed the client side is dead and there won't be anyone to send the callback to.
     sp<IBinder> surfaceControl = handle->surfaceControl.promote();
     if (surfaceControl) {
+        sp<Fence> prevFence = nullptr;
+
+        for (const auto& futureStruct : handle->previousReleaseFences) {
+            sp<Fence> currentFence = sp<Fence>::make(dup(futureStruct.get().drawFence));
+            if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) {
+                prevFence = currentFence;
+                handle->previousReleaseFence = prevFence;
+            } else if (prevFence != nullptr) {
+                // If both fences are signaled or both are unsignaled, we need to merge
+                // them to get an accurate timestamp.
+                if (prevFence->getStatus() != Fence::Status::Invalid &&
+                    prevFence->getStatus() == currentFence->getStatus()) {
+                    char fenceName[32] = {};
+                    snprintf(fenceName, 32, "%.28s", handle->name.c_str());
+                    sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence);
+                    if (mergedFence->isValid()) {
+                        handle->previousReleaseFence = mergedFence;
+                        prevFence = handle->previousReleaseFence;
+                    }
+                } else if (currentFence->getStatus() == Fence::Status::Unsignaled) {
+                    // If one fence has signaled and the other hasn't, the unsignaled
+                    // fence will approximately correspond with the correct timestamp.
+                    // There's a small race if both fences signal at about the same time
+                    // and their statuses are retrieved with unfortunate timing. However,
+                    // by this point, they will have both signaled and only the timestamp
+                    // will be slightly off; any dependencies after this point will
+                    // already have been met.
+                    handle->previousReleaseFence = currentFence;
+                }
+            }
+        }
+        handle->previousReleaseFences = {};
         FrameEventHistoryStats eventStats(handle->frameNumber,
                                           handle->gpuCompositionDoneFence->getSnapshot().fence,
                                           handle->compositorTiming, handle->refreshStartTime,
@@ -244,13 +178,10 @@
 }
 
 void TransactionCallbackInvoker::addPresentFence(const sp<Fence>& presentFence) {
-    std::lock_guard<std::mutex> lock(mMutex);
     mPresentFence = presentFence;
 }
 
-void TransactionCallbackInvoker::sendCallbacks() {
-    std::lock_guard lock(mMutex);
-
+void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
     // For each listener
     auto completedTransactionsItr = mCompletedTransactions.begin();
     while (completedTransactionsItr != mCompletedTransactions.end()) {
@@ -262,28 +193,14 @@
         auto transactionStatsItr = transactionStatsDeque.begin();
         while (transactionStatsItr != transactionStatsDeque.end()) {
             auto& transactionStats = *transactionStatsItr;
-
-            // If this transaction is still registering, it is not safe to send a callback
-            // because there could be surface controls that haven't been added to
-            // transaction stats or mPendingTransactions.
-            if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
-                break;
-            }
-
-            // If we are still waiting on the callback handles for this transaction, stop
-            // here because all transaction callbacks for the same listener must come in order
-            auto pendingTransactions = mPendingTransactions.find(listener);
-            if (pendingTransactions != mPendingTransactions.end() &&
-                pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
-                break;
+            if (onCommitOnly && !containsOnCommitCallbacks(transactionStats.callbackIds)) {
+                transactionStatsItr++;
+                continue;
             }
 
             // If the transaction has been latched
             if (transactionStats.latchTime >= 0 &&
                 !containsOnCommitCallbacks(transactionStats.callbackIds)) {
-                if (!mPresentFence) {
-                    break;
-                }
                 transactionStats.presentFence = mPresentFence;
             }
 
@@ -301,22 +218,13 @@
                 // keep it as an IBinder due to consistency reasons: if we
                 // interface_cast at the IPC boundary when reading a Parcel,
                 // we get pointers that compare unequal in the SF process.
-                interface_cast<ITransactionCompletedListener>(listenerStats.listener)
-                        ->onTransactionCompleted(listenerStats);
-                if (transactionStatsDeque.empty()) {
-                    listener->unlinkToDeath(mDeathRecipient);
-                    completedTransactionsItr =
-                            mCompletedTransactions.erase(completedTransactionsItr);
-                } else {
-                    completedTransactionsItr++;
-                }
-            } else {
-                completedTransactionsItr =
-                        mCompletedTransactions.erase(completedTransactionsItr);
+                BackgroundExecutor::getInstance().execute([stats = std::move(listenerStats)]() {
+                    interface_cast<ITransactionCompletedListener>(stats.listener)
+                            ->onTransactionCompleted(stats);
+                });
             }
-        } else {
-            completedTransactionsItr++;
         }
+        completedTransactionsItr++;
     }
 
     if (mPresentFence) {
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 6f4d812..5ef5475 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -18,7 +18,9 @@
 
 #include <condition_variable>
 #include <deque>
+#include <future>
 #include <mutex>
+#include <queue>
 #include <thread>
 #include <unordered_map>
 #include <unordered_set>
@@ -27,6 +29,7 @@
 
 #include <binder/IBinder.h>
 #include <gui/ITransactionCompletedListener.h>
+#include <renderengine/RenderEngine.h>
 #include <ui/Fence.h>
 
 namespace android {
@@ -41,7 +44,9 @@
     wp<IBinder> surfaceControl;
 
     bool releasePreviousBuffer = false;
+    std::string name;
     sp<Fence> previousReleaseFence;
+    std::vector<std::shared_future<renderengine::RenderEngineResult>> previousReleaseFences;
     nsecs_t acquireTime = -1;
     nsecs_t latchTime = -1;
     uint32_t transformHint = 0;
@@ -56,79 +61,36 @@
 
 class TransactionCallbackInvoker {
 public:
-    ~TransactionCallbackInvoker();
-
-    // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
-    // to be included in the callback. This functions should be call before attempting to register
-    // any callback handles.
-    status_t startRegistration(const ListenerCallbacks& listenerCallbacks);
-    // Ends the registration. After this is called, no more CallbackHandles will be registered.
-    // It is safe to send a callback if the Transaction doesn't have any Pending callback handles.
-    status_t endRegistration(const ListenerCallbacks& listenerCallbacks);
-
-    // Informs the TransactionCallbackInvoker that there is a Transaction with a CallbackHandle
-    // that needs to be latched and presented this frame. This function should be called once the
-    // layer has received the CallbackHandle so the TransactionCallbackInvoker knows not to send
-    // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
-    // presented.
-    status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
-    // Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented.
-    status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
-                                            const std::vector<JankData>& jankData);
-    status_t finalizeOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+    status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+                                const std::vector<JankData>& jankData);
+    status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
                                              std::deque<sp<CallbackHandle>>& outRemainingHandles);
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
     status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+    void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks);
 
     void addPresentFence(const sp<Fence>& presentFence);
 
-    void sendCallbacks();
-
-private:
-
-    bool isRegisteringTransaction(const sp<IBinder>& transactionListener,
-                                  const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
-
-    status_t findTransactionStats(const sp<IBinder>& listener,
-                                  const std::vector<CallbackId>& callbackIds,
-                                  TransactionStats** outTransactionStats) REQUIRES(mMutex);
+    void sendCallbacks(bool onCommitOnly);
+    void clearCompletedTransactions() {
+        mCompletedTransactions.clear();
+    }
 
     status_t addCallbackHandle(const sp<CallbackHandle>& handle,
-                               const std::vector<JankData>& jankData) REQUIRES(mMutex);
+                               const std::vector<JankData>& jankData);
 
-    status_t finalizeCallbackHandle(const sp<CallbackHandle>& handle,
-                                    const std::vector<JankData>& jankData) REQUIRES(mMutex);
 
-    class CallbackDeathRecipient : public IBinder::DeathRecipient {
-    public:
-        // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
-        // Death recipients needs a binderDied function.
-        //
-        // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary.
-        // sendObituary is only called if linkToDeath was called with a DeathRecipient.)
-        void binderDied(const wp<IBinder>& /*who*/) override {}
-    };
-    sp<CallbackDeathRecipient> mDeathRecipient =
-        new CallbackDeathRecipient();
-
-    std::mutex mMutex;
-    std::condition_variable_any mConditionVariable;
-
-    std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> mRegisteringTransactions
-            GUARDED_BY(mMutex);
-
-    std::unordered_map<
-            sp<IBinder>,
-            std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
-            IListenerHash>
-            mPendingTransactions GUARDED_BY(mMutex);
+private:
+    status_t findOrCreateTransactionStats(const sp<IBinder>& listener,
+                                          const std::vector<CallbackId>& callbackIds,
+                                          TransactionStats** outTransactionStats);
 
     std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
-            mCompletedTransactions GUARDED_BY(mMutex);
+        mCompletedTransactions;
 
-    sp<Fence> mPresentFence GUARDED_BY(mMutex);
+    sp<Fence> mPresentFence;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
new file mode 100644
index 0000000..fe3f3fc
--- /dev/null
+++ b/services/surfaceflinger/TransactionState.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/LayerState.h>
+
+namespace android {
+class CountDownLatch;
+
+struct TransactionState {
+    TransactionState(const FrameTimelineInfo& frameTimelineInfo,
+                     const Vector<ComposerState>& composerStates,
+                     const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+                     const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
+                     int64_t desiredPresentTime, bool isAutoTimestamp,
+                     const client_cache_t& uncacheBuffer, int64_t postTime, uint32_t permissions,
+                     bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
+                     int originPid, int originUid, uint64_t transactionId)
+          : frameTimelineInfo(frameTimelineInfo),
+            states(composerStates),
+            displays(displayStates),
+            flags(transactionFlags),
+            applyToken(applyToken),
+            inputWindowCommands(inputWindowCommands),
+            desiredPresentTime(desiredPresentTime),
+            isAutoTimestamp(isAutoTimestamp),
+            buffer(uncacheBuffer),
+            postTime(postTime),
+            permissions(permissions),
+            hasListenerCallbacks(hasListenerCallbacks),
+            listenerCallbacks(listenerCallbacks),
+            originPid(originPid),
+            originUid(originUid),
+            id(transactionId) {}
+
+    TransactionState() {}
+
+    void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
+
+    FrameTimelineInfo frameTimelineInfo;
+    Vector<ComposerState> states;
+    Vector<DisplayState> displays;
+    uint32_t flags;
+    sp<IBinder> applyToken;
+    InputWindowCommands inputWindowCommands;
+    int64_t desiredPresentTime;
+    bool isAutoTimestamp;
+    client_cache_t buffer;
+    int64_t postTime;
+    uint32_t permissions;
+    bool hasListenerCallbacks;
+    std::vector<ListenerCallbacks> listenerCallbacks;
+    int originPid;
+    int originUid;
+    uint64_t id;
+    std::shared_ptr<CountDownLatch> transactionCommittedSignal;
+};
+
+class CountDownLatch {
+public:
+    enum {
+        eSyncTransaction = 1 << 0,
+        eSyncInputWindows = 1 << 1,
+    };
+    explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
+
+    // True if there is no waiting condition after count down.
+    bool countDown(uint32_t flag) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        if (mFlags == 0) {
+            return true;
+        }
+        mFlags &= ~flag;
+        if (mFlags == 0) {
+            mCountDownComplete.notify_all();
+            return true;
+        }
+        return false;
+    }
+
+    // Return true if triggered.
+    bool wait_until(const std::chrono::seconds& timeout) const {
+        std::unique_lock<std::mutex> lock(mMutex);
+        const auto untilTime = std::chrono::system_clock::now() + timeout;
+        while (mFlags != 0) {
+            // Conditional variables can be woken up sporadically, so we check count
+            // to verify the wakeup was triggered by |countDown|.
+            if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+private:
+    uint32_t mFlags;
+    mutable std::condition_variable mCountDownComplete;
+    mutable std::mutex mMutex;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h
index 935502a..802d22d 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.h
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.h
@@ -22,6 +22,8 @@
 
 #include <unordered_map>
 
+#include "WpHash.h"
+
 namespace android {
 
 class Layer;
@@ -54,11 +56,6 @@
 
 private:
     mutable std::mutex mMutex;
-    struct WpHash {
-        size_t operator()(const wp<IBinder>& p) const {
-            return std::hash<IBinder*>()(p.unsafe_get());
-        }
-    };
 
     std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners
             GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index dc2aa58..b93d127 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -21,6 +21,7 @@
 
 namespace android {
 
+using gui::DisplayInfo;
 using gui::IWindowInfosListener;
 using gui::WindowInfo;
 
@@ -67,6 +68,7 @@
 }
 
 void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+                                                    const std::vector<DisplayInfo>& displayInfos,
                                                     bool shouldSync) {
     std::unordered_set<sp<IWindowInfosListener>, ISurfaceComposer::SpHash<IWindowInfosListener>>
             windowInfosListeners;
@@ -81,7 +83,7 @@
     mCallbacksPending = windowInfosListeners.size();
 
     for (const auto& listener : windowInfosListeners) {
-        listener->onWindowInfosChanged(windowInfos,
+        listener->onWindowInfosChanged(windowInfos, displayInfos,
                                        shouldSync ? mWindowInfosReportedListener : nullptr);
     }
 }
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 5e5796f..4e08393 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -23,6 +23,8 @@
 #include <utils/Mutex.h>
 #include <unordered_map>
 
+#include "WpHash.h"
+
 namespace android {
 
 class SurfaceFlinger;
@@ -33,7 +35,8 @@
     void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
     void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
 
-    void windowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, bool shouldSync);
+    void windowInfosChanged(const std::vector<gui::WindowInfo>&,
+                            const std::vector<gui::DisplayInfo>&, bool shouldSync);
 
 protected:
     void binderDied(const wp<IBinder>& who) override;
@@ -41,12 +44,6 @@
 private:
     void windowInfosReported();
 
-    struct WpHash {
-        size_t operator()(const wp<IBinder>& p) const {
-            return std::hash<IBinder*>()(p.unsafe_get());
-        }
-    };
-
     const sp<SurfaceFlinger> mSf;
     std::mutex mListenersMutex;
     std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash>
diff --git a/libs/ui/Size.cpp b/services/surfaceflinger/WpHash.h
similarity index 71%
copy from libs/ui/Size.cpp
copy to services/surfaceflinger/WpHash.h
index d2996d1..86777b7 100644
--- a/libs/ui/Size.cpp
+++ b/services/surfaceflinger/WpHash.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+#pragma once
 
-namespace android::ui {
+namespace android {
 
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+struct WpHash {
+    size_t operator()(const wp<IBinder>& p) const { return std::hash<IBinder*>()(p.unsafe_get()); }
+};
 
-} // namespace android::ui
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/common.proto b/services/surfaceflinger/layerproto/common.proto
index 1c73a9f..a6d8d61 100644
--- a/services/surfaceflinger/layerproto/common.proto
+++ b/services/surfaceflinger/layerproto/common.proto
@@ -18,6 +18,11 @@
 option optimize_for = LITE_RUNTIME;
 package android.surfaceflinger;
 
+message RegionProto {
+    reserved 1; // Previously: uint64 id
+    repeated RectProto rect = 2;
+}
+
 message RectProto {
   int32 left   = 1;
   int32 top    = 2;
@@ -36,4 +41,51 @@
   float dsdy = 3;
   float dtdy = 4;
   int32 type = 5;
-}
\ No newline at end of file
+}
+
+message ColorProto {
+    float r = 1;
+    float g = 2;
+    float b = 3;
+    float a = 4;
+}
+
+message InputWindowInfoProto {
+    uint32 layout_params_flags = 1;
+    int32 layout_params_type = 2;
+    RectProto frame = 3;
+    RegionProto touchable_region = 4;
+
+    int32 surface_inset = 5;
+    bool visible = 6;
+    bool can_receive_keys = 7 [deprecated = true];
+    bool focusable = 8;
+    bool has_wallpaper = 9;
+
+    float global_scale_factor = 10;
+    float window_x_scale = 11 [deprecated = true];
+    float window_y_scale = 12 [deprecated = true];
+
+    int32 crop_layer_id = 13;
+    bool replace_touchable_region_with_crop = 14;
+    RectProto touchable_region_crop = 15;
+    TransformProto transform = 16;
+}
+
+message BlurRegion {
+    uint32 blur_radius = 1;
+    uint32 corner_radius_tl = 2;
+    uint32 corner_radius_tr = 3;
+    uint32 corner_radius_bl = 4;
+    float corner_radius_br = 5;
+    float alpha = 6;
+    int32 left = 7;
+    int32 top = 8;
+    int32 right = 9;
+    int32 bottom = 10;
+}
+
+message ColorTransformProto {
+    // This will be a 4x4 matrix of float values
+    repeated float val = 1;
+}
diff --git a/services/surfaceflinger/layerproto/display.proto b/services/surfaceflinger/layerproto/display.proto
index ee8830e..c8cd926 100644
--- a/services/surfaceflinger/layerproto/display.proto
+++ b/services/surfaceflinger/layerproto/display.proto
@@ -33,4 +33,6 @@
     RectProto layer_stack_space_rect = 5;
 
     TransformProto transform = 6;
+
+    bool is_virtual = 7;
 }
diff --git a/libs/ui/Size.cpp b/services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
similarity index 68%
copy from libs/ui/Size.cpp
copy to services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
index d2996d1..3e9ca52 100644
--- a/libs/ui/Size.cpp
+++ b/services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
-
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+// disable the warnings emitted from the protobuf headers. This file should be included instead of
+// directly including the generated header file
+#pragma GCC system_header
+#include <transactions.pb.h>
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 057eabb..4529905 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -143,11 +143,6 @@
   float y = 2;
 }
 
-message RegionProto {
-  reserved 1;  // Previously: uint64 id
-  repeated RectProto rect = 2;
-}
-
 message FloatRectProto {
   float left = 1;
   float top = 2;
@@ -162,13 +157,6 @@
   int32 format = 4;
 }
 
-message ColorProto {
-  float r = 1;
-  float g = 2;
-  float b = 3;
-  float a = 4;
-}
-
 message BarrierLayerProto {
   // layer id the barrier is waiting on.
   int32 id = 1;
@@ -176,42 +164,3 @@
   uint64 frame_number = 2;
 }
 
-message InputWindowInfoProto {
-    uint32 layout_params_flags = 1;
-    uint32 layout_params_type = 2;
-    RectProto frame = 3;
-    RegionProto touchable_region = 4;
-
-    uint32 surface_inset = 5;
-    bool visible = 6;
-    bool can_receive_keys = 7  [deprecated=true];
-    bool focusable = 8;
-    bool has_wallpaper = 9;
-
-    float global_scale_factor = 10;
-    float window_x_scale = 11 [deprecated=true];
-    float window_y_scale = 12 [deprecated=true];
-
-    uint32 crop_layer_id = 13;
-    bool replace_touchable_region_with_crop = 14;
-    RectProto touchable_region_crop = 15;
-    TransformProto transform = 16;
-}
-
-message ColorTransformProto {
-  // This will be a 4x4 matrix of float values
-  repeated float val = 1;
-}
-
-message BlurRegion {
-    uint32 blur_radius = 1;
-    uint32 corner_radius_tl = 2;
-    uint32 corner_radius_tr = 3;
-    uint32 corner_radius_bl = 4;
-    float corner_radius_br = 5;
-    float alpha = 6;
-    int32 left = 7;
-    int32 top = 8;
-    int32 right = 9;
-    int32 bottom = 10;
-}
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
new file mode 100644
index 0000000..e31b502
--- /dev/null
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+option optimize_for = LITE_RUNTIME;
+
+import "frameworks/native/services/surfaceflinger/layerproto/common.proto";
+
+package android.surfaceflinger.proto;
+
+/* Represents a file full of surface flinger transactions.
+   Encoded, it should start with 0x54 0x4E 0x58 0x54 0x52 0x41 0x43 0x45 (.TNXTRACE), such
+   that they can be easily identified. */
+message TransactionTraceFile {
+    /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+       (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+        constants into .proto files. */
+    enum MagicNumber {
+        INVALID = 0;
+        MAGIC_NUMBER_L = 0x54584E54; /* TNXT (little-endian ASCII) */
+        MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+    }
+
+    fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+    repeated TransactionTraceEntry entry = 2;
+}
+
+message TransactionTraceEntry {
+    int64 elapsed_realtime_nanos = 1;
+    int64 vsync_id = 2;
+    repeated TransactionState transactions = 3;
+    repeated LayerCreationArgs added_layers = 4;
+    repeated int32 removed_layers = 5;
+    repeated DisplayState added_displays = 6;
+    repeated int32 removed_displays = 7;
+}
+
+message LayerCreationArgs {
+    int32 layer_id = 1;
+    string name = 2;
+    uint32 flags = 3;
+    int32 parent_id = 4;
+    int32 mirror_from_id = 5;
+}
+
+message TransactionState {
+    int32 pid = 1;
+    int32 uid = 2;
+    int64 vsync_id = 3;
+    int32 input_event_id = 4;
+    int64 post_time = 5;
+    repeated LayerState layer_changes = 6;
+    repeated DisplayState display_changes = 7;
+}
+
+// Keep insync with layer_state_t
+message LayerState {
+    int32 layer_id = 1;
+    // Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
+    // and the next 32 bits are in ChangesMsb. This is needed because enums have to be
+    // 32 bits and there's no nice way to put 64bit constants into .proto files.
+    enum ChangesLsb {
+        eChangesLsbNone = 0;
+        ePositionChanged = 0x00000001;
+        eLayerChanged = 0x00000002;
+        eSizeChanged = 0x00000004;
+        eAlphaChanged = 0x00000008;
+        eMatrixChanged = 0x00000010;
+        eTransparentRegionChanged = 0x00000020;
+        eFlagsChanged = 0x00000040;
+        eLayerStackChanged = 0x00000080;
+        eReleaseBufferListenerChanged = 0x00000400;
+        eShadowRadiusChanged = 0x00000800;
+        eLayerCreated = 0x00001000;
+        eBufferCropChanged = 0x00002000;
+        eRelativeLayerChanged = 0x00004000;
+        eReparent = 0x00008000;
+        eColorChanged = 0x00010000;
+        eDestroySurface = 0x00020000;
+        eTransformChanged = 0x00040000;
+        eTransformToDisplayInverseChanged = 0x00080000;
+        eCropChanged = 0x00100000;
+        eBufferChanged = 0x00200000;
+        eAcquireFenceChanged = 0x00400000;
+        eDataspaceChanged = 0x00800000;
+        eHdrMetadataChanged = 0x01000000;
+        eSurfaceDamageRegionChanged = 0x02000000;
+        eApiChanged = 0x04000000;
+        eSidebandStreamChanged = 0x08000000;
+        eColorTransformChanged = 0x10000000;
+        eHasListenerCallbacksChanged = 0x20000000;
+        eInputInfoChanged = 0x40000000;
+        eCornerRadiusChanged = -2147483648; // 0x80000000; (proto stores enums as signed int)
+    };
+    enum ChangesMsb {
+        eChangesMsbNone = 0;
+        eDestinationFrameChanged = 0x1;
+        eCachedBufferChanged = 0x2;
+        eBackgroundColorChanged = 0x4;
+        eMetadataChanged = 0x8;
+        eColorSpaceAgnosticChanged = 0x10;
+        eFrameRateSelectionPriority = 0x20;
+        eFrameRateChanged = 0x40;
+        eBackgroundBlurRadiusChanged = 0x80;
+        eProducerDisconnect = 0x100;
+        eFixedTransformHintChanged = 0x200;
+        eFrameNumberChanged = 0x400;
+        eBlurRegionsChanged = 0x800;
+        eAutoRefreshChanged = 0x1000;
+        eStretchChanged = 0x2000;
+        eTrustedOverlayChanged = 0x4000;
+        eDropInputModeChanged = 0x8000;
+    };
+    uint64 what = 2;
+    float x = 3;
+    float y = 4;
+    int32 z = 5;
+    uint32 w = 6;
+    uint32 h = 7;
+    uint32 layer_stack = 8;
+
+    enum Flags {
+        eFlagsNone = 0;
+        eLayerHidden = 0x01;
+        eLayerOpaque = 0x02;
+        eLayerSkipScreenshot = 0x40;
+        eLayerSecure = 0x80;
+        eEnableBackpressure = 0x100;
+    };
+    uint32 flags = 9;
+    uint32 mask = 10;
+
+    message Matrix22 {
+        float dsdx = 1;
+        float dtdx = 2;
+        float dtdy = 3;
+        float dsdy = 4;
+    };
+    Matrix22 matrix = 11;
+    float corner_radius = 12;
+    uint32 background_blur_radius = 13;
+    int32 parent_id = 14;
+    int32 relative_parent_id = 15;
+
+    float alpha = 16;
+    message Color3 {
+        float r = 1;
+        float g = 2;
+        float b = 3;
+    }
+    Color3 color = 17;
+    RegionProto transparent_region = 18;
+    uint32 transform = 19;
+    bool transform_to_display_inverse = 20;
+    RectProto crop = 21;
+
+    message BufferData {
+        uint64 buffer_id = 1;
+        uint32 width = 2;
+        uint32 height = 3;
+        uint64 frame_number = 4;
+
+        enum BufferDataChange {
+            BufferDataChangeNone = 0;
+            fenceChanged = 0x01;
+            frameNumberChanged = 0x02;
+            cachedBufferChanged = 0x04;
+        }
+        uint32 flags = 5;
+        uint64 cached_buffer_id = 6;
+    }
+    BufferData buffer_data = 22;
+    int32 api = 23;
+    bool has_sideband_stream = 24;
+    ColorTransformProto color_transform = 25;
+    repeated BlurRegion blur_regions = 26;
+
+    message Transform {
+        float dsdx = 1;
+        float dtdx = 2;
+        float dtdy = 3;
+        float dsdy = 4;
+        float tx = 5;
+        float ty = 6;
+    }
+    message WindowInfo {
+        uint32 layout_params_flags = 1;
+        int32 layout_params_type = 2;
+        RegionProto touchable_region = 3;
+        int32 surface_inset = 4;
+        bool focusable = 5;
+        bool has_wallpaper = 6;
+        float global_scale_factor = 7;
+        int32 crop_layer_id = 8;
+        bool replace_touchable_region_with_crop = 9;
+        RectProto touchable_region_crop = 10;
+        Transform transform = 11;
+    }
+    WindowInfo window_info_handle = 27;
+    float bg_color_alpha = 28;
+    int32 bg_color_dataspace = 29;
+    bool color_space_agnostic = 30;
+    float shadow_radius = 31;
+    int32 frame_rate_selection_priority = 32;
+    float frame_rate = 33;
+    int32 frame_rate_compatibility = 34;
+    int32 change_frame_rate_strategy = 35;
+    uint32 fixed_transform_hint = 36;
+    uint64 frame_number = 37;
+    bool auto_refresh = 38;
+    bool is_trusted_overlay = 39;
+    RectProto buffer_crop = 40;
+    RectProto destination_frame = 41;
+
+    enum DropInputMode {
+        NONE = 0;
+        ALL = 1;
+        OBSCURED = 2;
+    };
+    DropInputMode drop_input_mode = 42;
+}
+
+message DisplayState {
+    enum Changes {
+        eChangesNone = 0;
+        eSurfaceChanged = 0x01;
+        eLayerStackChanged = 0x02;
+        eDisplayProjectionChanged = 0x04;
+        eDisplaySizeChanged = 0x08;
+        eFlagsChanged = 0x10;
+    };
+    int32 id = 1;
+    uint32 what = 2;
+    uint32 flags = 3;
+    uint32 layer_stack = 4;
+    uint32 orientation = 5;
+    RectProto layer_stack_space_rect = 6;
+    RectProto oriented_display_space_rect = 7;
+    uint32 width = 8;
+    uint32 height = 9;
+}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 78f8a2f..7702ea2 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -414,16 +414,6 @@
     prop_name: "ro.surface_flinger.supports_background_blur"
 }
 
-# Indicates whether Scheduler should use frame rate API when adjusting the
-# display refresh rate.
-prop {
-    api_name: "use_frame_rate_api"
-    type: Boolean
-    scope: Public
-    access: Readonly
-    prop_name: "ro.surface_flinger.use_frame_rate_api"
-}
-
 # Sets the timeout used to rate limit DISPLAY_UPDATE_IMMINENT Power HAL notifications.
 # SurfaceFlinger wakeups will trigger this boost whenever they are separated by more than this
 # duration (specified in milliseconds). A value of 0 disables the rate limit, and will result in
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9c567d6..bf1e7e2 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -152,10 +152,6 @@
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
-    api_name: "use_frame_rate_api"
-    prop_name: "ro.surface_flinger.use_frame_rate_api"
-  }
-  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
     deprecated: true
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index ba60a7d..640b9fb 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -136,10 +136,6 @@
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
-    api_name: "use_frame_rate_api"
-    prop_name: "ro.surface_flinger.use_frame_rate_api"
-  }
-  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
     deprecated: true
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 32ad873..7b86229 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -60,7 +60,7 @@
         "android.hardware.graphics.composer@2.1",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V2-ndk",
+        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.common@1.2",
         "libandroid",
         "libbase",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index fa3f0e7..d33bc10 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -93,7 +93,7 @@
         ASSERT_TRUE(mBGSurfaceControl->isValid());
 
         Transaction t;
-        t.setDisplayLayerStack(mDisplay, 0);
+        t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK);
         ASSERT_EQ(NO_ERROR,
                   t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
     }
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 93656f3..9fa0452 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -33,7 +33,7 @@
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
             t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
             t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
         });
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 84fea6c..ce94dab 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -159,10 +159,9 @@
                                                  {0, 0, static_cast<int32_t>(width),
                                                   static_cast<int32_t>(height)},
                                                  Color::RED);
-        transaction->setLayerStack(mSurfaceControl, 0)
+        transaction->setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
                 .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
-                .setBuffer(mSurfaceControl, gb)
-                .setAcquireFence(mSurfaceControl, fence)
+                .setBuffer(mSurfaceControl, gb, fence)
                 .show(mSurfaceControl)
                 .addTransactionCompletedCallback(mCallbackHelper.function,
                                                  mCallbackHelper.getContext());
@@ -232,7 +231,7 @@
         mDisplayHeight = mode.resolution.getHeight();
 
         Transaction setupTransaction;
-        setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
+        setupTransaction.setDisplayLayerStack(mPrimaryDisplay, ui::DEFAULT_LAYER_STACK);
         setupTransaction.apply();
     }
 
@@ -310,10 +309,9 @@
                                              Color::RED);
 
     Transaction transaction;
-    transaction.setLayerStack(sc, 0)
+    transaction.setLayerStack(sc, ui::DEFAULT_LAYER_STACK)
             .setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
-            .setBuffer(sc, gb)
-            .setAcquireFence(sc, fence)
+            .setBuffer(sc, gb, fence)
             .show(sc)
             .addTransactionCompletedCallback(helper1.function, helper1.getContext());
 
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 7ff041e..5c16fee 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -26,6 +26,7 @@
 namespace android {
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
+using SCHash = SurfaceComposerClient::SCHash;
 
 ::testing::Environment* const binderEnv =
         ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
@@ -66,8 +67,7 @@
                     return err;
                 }
 
-                transaction.setBuffer(layer, buffer);
-                transaction.setAcquireFence(layer, fence);
+                transaction.setBuffer(layer, buffer, fence);
             }
 
             if (setBackgroundColor) {
@@ -103,6 +103,24 @@
         }
     }
 
+    static void waitForCommitCallback(
+            CallbackHelper& helper,
+            const std::unordered_set<sp<SurfaceControl>, SCHash>& committedSc) {
+        CallbackData callbackData;
+        ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+
+        const auto& surfaceControlStats = callbackData.surfaceControlStats;
+
+        ASSERT_EQ(surfaceControlStats.size(), committedSc.size()) << "wrong number of surfaces";
+
+        for (const auto& stats : surfaceControlStats) {
+            ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
+            const auto& expectedSc = committedSc.find(stats.surfaceControl);
+            ASSERT_NE(expectedSc, committedSc.end()) << "unexpected surface control";
+        }
+    }
+
     DisplayEventReceiver mDisplayEventReceiver;
     int mEpollFd;
 
@@ -1030,4 +1048,85 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
 }
 
+// b202394221
+TEST_F(LayerCallbackTest, EmptyBufferStateChanges) {
+    sp<SurfaceControl> bufferLayer, emptyBufferLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        int err = fillTransaction(transaction, &callback, bufferLayer);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        ui::Size bufferSize = getBufferSize();
+
+        TransactionUtils::setFrame(transaction, bufferLayer,
+                                   Rect(0, 0, bufferSize.width, bufferSize.height),
+                                   Rect(0, 0, 32, 32));
+        transaction.setPosition(emptyBufferLayer, 1 + i, 2 + i);
+        transaction.apply();
+
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, bufferLayer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                     : ExpectedResult::PreviousBuffer::RELEASED);
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, emptyBufferLayer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED,
+                            ExpectedResult::PreviousBuffer::NOT_RELEASED);
+
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+// b202394221
+TEST_F(LayerCallbackTest, DISABLED_NonBufferLayerStateChanges) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createColorLayer("ColorLayer", Color::RED));
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    transaction.setPosition(layer, 1, 2);
+    transaction.apply();
+
+    ExpectedResult expected;
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    sp<SurfaceControl> offscreenLayer =
+            createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceBufferState, layer.get());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, true);
+    err |= fillTransaction(transaction, &callback, offscreenLayer, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.reparent(offscreenLayer, nullptr)
+            .addTransactionCommittedCallback(callback.function, callback.getContext());
+    transaction.apply();
+
+    std::unordered_set<sp<SurfaceControl>, SCHash> committedSc;
+    committedSc.insert(layer);
+    committedSc.insert(offscreenLayer);
+    EXPECT_NO_FATAL_FAILURE(waitForCommitCallback(callback, committedSc));
+}
 } // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index c8eeac6..0e2bc3d 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1353,7 +1353,7 @@
         return;
     }
 
-    Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+    Transaction().setBuffer(layer, buffer, fence).apply();
 
     status_t status = fence->wait(1000);
     ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
@@ -1375,7 +1375,7 @@
 
     sp<Fence> fence = Fence::NO_FENCE;
 
-    Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+    Transaction().setBuffer(layer, buffer, fence).apply();
 
     auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 0bc8fe7..6bd7920 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -266,7 +266,7 @@
     sp<IBinder> mDisplay;
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
-    uint32_t mDisplayLayerStack;
+    ui::LayerStack mDisplayLayerStack = ui::DEFAULT_LAYER_STACK;
     Rect mDisplayRect = Rect::INVALID_RECT;
 
     // leave room for ~256 layers
@@ -294,8 +294,6 @@
         // vsyncs.
         mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
 
-        mDisplayLayerStack = 0;
-
         mBlackBgSurface =
                 createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
                               PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 25f3bb9..9cb617a 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -763,7 +763,8 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
 
-    Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
+    const auto layerStack = ui::LayerStack::fromValue(mDisplayLayerStack.id + 1);
+    Transaction().setLayerStack(layer, layerStack).apply();
     {
         SCOPED_TRACE("non-existing layer stack");
         getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index ee4d367..e1a7ecc 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -63,7 +63,7 @@
         TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
             t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
 
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index b7a9271..a921aa8 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -36,7 +36,7 @@
         mParentLayer = createColorLayer("Parent layer", Color::RED);
         mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get());
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
             t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
             t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
             t.setPosition(mChildLayer, 50, 50);
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 08de01c..1ed6c65 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -52,7 +52,7 @@
         mColorLayer = 0;
     }
 
-    void createDisplay(const ui::Size& layerStackSize, uint32_t layerStack) {
+    void createDisplay(const ui::Size& layerStackSize, ui::LayerStack layerStack) {
         mVirtualDisplay =
                 SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
         asTransaction([&](Transaction& t) {
@@ -63,7 +63,7 @@
         });
     }
 
-    void createColorLayer(uint32_t layerStack) {
+    void createColorLayer(ui::LayerStack layerStack) {
         mColorLayer =
                 createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
                               PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
@@ -90,8 +90,9 @@
 };
 
 TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
-    createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */);
-    createColorLayer(1 /* layerStack */);
+    constexpr ui::LayerStack kLayerStack{1u};
+    createDisplay(mMainDisplayState.layerStackSpaceRect, kLayerStack);
+    createColorLayer(kLayerStack);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
 
@@ -113,8 +114,8 @@
 
     // Assumption here is that the new mirrored display has the same layer stack rect as the
     // primary display that it is mirroring.
-    createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */);
-    createColorLayer(0 /* layerStack */);
+    createDisplay(mMainDisplayState.layerStackSpaceRect, ui::DEFAULT_LAYER_STACK);
+    createColorLayer(ui::DEFAULT_LAYER_STACK);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
 
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index fde6e6e..50a4092 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -43,7 +43,7 @@
         mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
             t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
             t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
         });
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index 309ab2f..027a15e 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -31,7 +31,7 @@
 public:
     static void function(void* callbackContext, ReleaseCallbackId callbackId,
                          const sp<Fence>& releaseFence,
-                         uint32_t /*currentMaxAcquiredBufferCount*/) {
+                         std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {
         if (!callbackContext) {
             FAIL() << "failed to get callback context";
         }
@@ -85,9 +85,7 @@
                              sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
                              ReleaseBufferCallbackHelper& releaseCallback) {
         Transaction t;
-        t.setFrameNumber(layer, id.framenumber);
-        t.setBuffer(layer, buffer, id, releaseCallback.getCallback());
-        t.setAcquireFence(layer, fence);
+        t.setBuffer(layer, buffer, fence, id.framenumber, releaseCallback.getCallback());
         t.addTransactionCompletedCallback(callback.function, callback.getContext());
         t.apply();
     }
@@ -302,8 +300,8 @@
     nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     Transaction t;
-    t.setBuffer(layer, firstBuffer, firstBufferCallbackId, releaseCallback->getCallback());
-    t.setAcquireFence(layer, Fence::NO_FENCE);
+    t.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+                releaseCallback->getCallback());
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
     t.setDesiredPresentTime(time);
@@ -318,8 +316,8 @@
     // Dropping frames in transaction queue emits a callback
     sp<GraphicBuffer> secondBuffer = getBuffer();
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
-    t.setBuffer(layer, secondBuffer, secondBufferCallbackId, releaseCallback->getCallback());
-    t.setAcquireFence(layer, Fence::NO_FENCE);
+    t.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+                releaseCallback->getCallback());
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
     t.setDesiredPresentTime(time);
@@ -361,10 +359,8 @@
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
 
     Transaction transaction1;
-    transaction1.setFrameNumber(layer, secondBufferCallbackId.framenumber);
-    transaction1.setBuffer(layer, secondBuffer, secondBufferCallbackId,
+    transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
                            releaseCallback->getCallback());
-    transaction1.setAcquireFence(layer, Fence::NO_FENCE);
     transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext());
 
     // Set a different TransactionCompletedListener to mimic a second process
@@ -389,4 +385,84 @@
     ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
 
+TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+    // Create transaction with a buffer.
+    Transaction transaction;
+    transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+                          releaseCallback->getCallback());
+
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+    // Call setBuffer on the same transaction with a different buffer.
+    transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+                          releaseCallback->getCallback());
+
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+    // Create transaction with a buffer.
+    Transaction transaction1;
+    transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+                           releaseCallback->getCallback());
+
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+    // Create a second transaction with a new buffer for the same layer.
+    Transaction transaction2;
+    transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+                           releaseCallback->getCallback());
+
+    // merge transaction1 into transaction2 so ensure we get a proper buffer release callback.
+    transaction1.merge(std::move(transaction2));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) {
+    sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
+    sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();
+
+    TransactionCompletedListener::setInstance(firstCompletedListener);
+
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+    Transaction transaction1;
+    transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+                           releaseCallback->getCallback());
+
+    // Sent a second buffer to allow the first buffer to get released.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+    Transaction transaction2;
+    transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+                           releaseCallback->getCallback());
+
+    // Set a different TransactionCompletedListener to mimic a second process
+    TransactionCompletedListener::setInstance(secondCompletedListener);
+    Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply();
+
+    // Make sure we can still get the release callback even though the merge happened in a different
+    // process.
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 2d5b502..f9b3185 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -55,7 +55,7 @@
         TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
             t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
 
@@ -556,7 +556,7 @@
     Transaction()
             .show(redLayer)
             .show(secureLayer)
-            .setLayerStack(redLayer, 0)
+            .setLayerStack(redLayer, ui::DEFAULT_LAYER_STACK)
             .setLayer(redLayer, INT32_MAX)
             .apply();
 
@@ -648,7 +648,7 @@
     Transaction()
             .show(layer)
             .hide(mFGSurfaceControl)
-            .setLayerStack(layer, 0)
+            .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setCrop(layer, bounds)
@@ -695,7 +695,7 @@
             .show(layer)
             .show(childLayer)
             .hide(mFGSurfaceControl)
-            .setLayerStack(layer, 0)
+            .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index a424059..28e8b8c 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -19,6 +19,7 @@
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
 
+#include <android-base/stringprintf.h>
 #include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <gtest/gtest.h>
@@ -56,10 +57,7 @@
 const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
 constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
 constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface";
-constexpr auto UNIQUE_TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface#0";
-constexpr auto UNIQUE_TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface#0";
 constexpr auto LAYER_NAME = "Layer Create and Delete Test";
-constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
 
 constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
 
@@ -105,11 +103,15 @@
     system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
 }
 
+std::string getUniqueName(const std::string& name, const Increment& increment) {
+    return base::StringPrintf("%s#%d", name.c_str(), increment.surface_creation().id());
+}
+
 int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
     int32_t layerId = 0;
     for (const auto& increment : capturedTrace.increment()) {
         if (increment.increment_case() == increment.kSurfaceCreation) {
-            if (increment.surface_creation().name() == surfaceName) {
+            if (increment.surface_creation().name() == getUniqueName(surfaceName, increment)) {
                 layerId = increment.surface_creation().id();
             }
         }
@@ -283,7 +285,7 @@
     ASSERT_TRUE(mFGSurfaceControl->isValid());
 
     Transaction t;
-    t.setDisplayLayerStack(display, 0);
+    t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
     ASSERT_EQ(NO_ERROR,
               t.setLayer(mBGSurfaceControl, INT_MAX - 3)
                       .show(mBGSurfaceControl)
@@ -293,8 +295,8 @@
 }
 
 void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
-    mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_BG_SURFACE_NAME);
-    mFGLayerId = getSurfaceId(trace, UNIQUE_TEST_FG_SURFACE_NAME);
+    mBGLayerId = getSurfaceId(trace, TEST_BG_SURFACE_NAME);
+    mFGLayerId = getSurfaceId(trace, TEST_FG_SURFACE_NAME);
 }
 
 void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
@@ -380,7 +382,7 @@
 }
 
 void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
-    t.setLayerStack(mBGSurfaceControl, STACK_UPDATE);
+    t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE));
 }
 
 void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
@@ -752,9 +754,7 @@
 }
 
 bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
-    bool isMatch(increment.surface_creation().name() == UNIQUE_LAYER_NAME &&
-            increment.surface_creation().w() == SIZE_UPDATE &&
-            increment.surface_creation().h() == SIZE_UPDATE);
+    bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment));
     if (isMatch && !foundSurface) {
         foundSurface = true;
     } else if (isMatch && foundSurface) {
@@ -808,7 +808,7 @@
                     break;
                 case Increment::IncrementCase::kSurfaceDeletion:
                     // Find the id of created surface.
-                    targetId = getSurfaceId(trace, UNIQUE_LAYER_NAME);
+                    targetId = getSurfaceId(trace, LAYER_NAME);
                     foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
                     break;
                 case Increment::IncrementCase::kDisplayCreation:
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 89f6086..60cffb1 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -65,7 +65,7 @@
 
                 SurfaceComposerClient::Transaction t;
                 t.setDisplaySurface(vDisplay, producer);
-                t.setDisplayLayerStack(vDisplay, 0);
+                t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
                 t.setDisplayProjection(vDisplay, displayState.orientation,
                                        Rect(displayState.layerStackSpaceRect), Rect(resolution));
                 t.apply();
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index 15b79be..bb52245 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -22,6 +22,7 @@
 
 namespace android {
 using Transaction = SurfaceComposerClient::Transaction;
+using gui::DisplayInfo;
 using gui::WindowInfo;
 
 class WindowInfosListenerTest : public ::testing::Test {
@@ -40,7 +41,8 @@
 
     struct SyncWindowInfosListener : public gui::WindowInfosListener {
     public:
-        void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+        void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+                                  const std::vector<DisplayInfo>&) override {
             windowInfosPromise.set_value(windowInfos);
         }
 
@@ -84,7 +86,7 @@
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
 
     Transaction()
-            .setLayerStack(surfaceControl, 0)
+            .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
             .show(surfaceControl)
             .setLayer(surfaceControl, INT32_MAX - 1)
             .setInputWindowInfo(surfaceControl, windowInfo)
@@ -112,7 +114,7 @@
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
     const Rect crop(0, 0, 100, 100);
     Transaction()
-            .setLayerStack(surfaceControl, 0)
+            .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
             .show(surfaceControl)
             .setLayer(surfaceControl, INT32_MAX - 1)
             .setCrop(surfaceControl, crop)
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2551a19..168b576 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -12,10 +12,10 @@
     defaults: ["surfaceflinger_defaults"],
     test_suites: ["device-tests"],
     srcs: [
-         "FakeComposerClient.cpp",
-         "FakeComposerService.cpp",
-         "FakeComposerUtils.cpp",
-         "SFFakeHwc_test.cpp"
+        "FakeComposerClient.cpp",
+        "FakeComposerService.cpp",
+        "FakeComposerUtils.cpp",
+        "SFFakeHwc_test.cpp",
     ],
     require_root: true,
     shared_libs: [
@@ -23,12 +23,14 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
+        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
         "android.hardware.graphics.mapper@4.0",
         "android.hardware.power@1.3",
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libcutils",
         "libfmq",
         "libgui",
@@ -43,15 +45,18 @@
     ],
     static_libs: [
         "android.hardware.graphics.composer@2.1-resources",
+        "libaidlcommonsupport",
         "libcompositionengine",
         "libgmock",
         "libperfetto_client_experimental",
         "librenderengine",
         "libtrace_proto",
+        "libaidlcommonsupport",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.4-command-buffer",
         "android.hardware.graphics.composer@2.4-hal",
+        "android.hardware.graphics.composer3-command-buffer",
         "libsurfaceflinger_headers",
     ],
 }
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 162711d..b3b4ec1 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -279,7 +279,7 @@
     }
 
     bool waitForHotplugEvent(Display displayId, bool connected) {
-        return waitForHotplugEvent(PhysicalDisplayId(displayId), connected);
+        return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected);
     }
 
     bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
@@ -305,7 +305,7 @@
     }
 
     bool waitForModeChangedEvent(Display display, int32_t modeId) {
-        PhysicalDisplayId displayId(display);
+        PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display);
         int waitCount = 20;
         while (waitCount--) {
             while (!mReceivedDisplayEvents.empty()) {
@@ -363,7 +363,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -426,7 +426,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -479,7 +479,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -534,7 +534,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -586,7 +586,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -651,7 +651,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -703,7 +703,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -750,7 +750,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -797,7 +797,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -1195,7 +1195,7 @@
         fillSurfaceRGBA8(mFGSurfaceControl, RED);
 
         Transaction t;
-        t.setDisplayLayerStack(display, 0);
+        t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
         t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
         t.show(mBGSurfaceControl);
@@ -1342,7 +1342,7 @@
         ALOGD("TransactionTest::SetLayerStack");
         {
             TransactionScope ts(*sFakeComposer);
-            ts.setLayerStack(mFGSurfaceControl, 1);
+            ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1});
         }
 
         // Foreground layer should have disappeared.
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9e704c3..bba880e 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -23,7 +23,10 @@
 
 cc_test {
     name: "libsurfaceflinger_unittest",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "skia_renderengine_deps",
+        "surfaceflinger_defaults",
+    ],
     test_suites: ["device-tests"],
     sanitize: {
         // Using the address sanitizer not only helps uncover issues in the code
@@ -52,8 +55,10 @@
         "DisplayTransactionTest.cpp",
         "DisplayDevice_GetBestColorModeTest.cpp",
         "DisplayDevice_InitiateModeChange.cpp",
+        "DisplayDevice_SetDisplayBrightnessTest.cpp",
         "DisplayDevice_SetProjectionTest.cpp",
         "EventThreadTest.cpp",
+        "FlagManagerTest.cpp",
         "FpsReporterTest.cpp",
         "FpsTest.cpp",
         "FramebufferSurfaceTest.cpp",
@@ -67,10 +72,11 @@
         "MessageQueueTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
+        "SurfaceFlinger_DisplayModeSwitching.cpp",
+        "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
         "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
-        "SurfaceFlinger_HandleTransactionLockedTest.cpp",
-        "SurfaceFlinger_NotifyPowerBoostTest.cpp",
         "SurfaceFlinger_HotplugTest.cpp",
+        "SurfaceFlinger_NotifyPowerBoostTest.cpp",
         "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
         "SurfaceFlinger_SetDisplayStateTest.cpp",
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
@@ -87,7 +93,9 @@
         "TimerTest.cpp",
         "TransactionApplicationTest.cpp",
         "TransactionFrameTracerTest.cpp",
+        "TransactionProtoParserTest.cpp",
         "TransactionSurfaceFrameTest.cpp",
+        "TransactionTracingTest.cpp",
         "TunnelModeEnabledReporterTest.cpp",
         "StrongTypingTest.cpp",
         "VSyncDispatchTimerQueueTest.cpp",
@@ -102,7 +110,6 @@
         "mock/MockEventThread.cpp",
         "mock/MockFrameTimeline.cpp",
         "mock/MockFrameTracer.cpp",
-        "mock/MockMessageQueue.cpp",
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
@@ -111,15 +118,20 @@
         "mock/system/window/MockNativeWindow.cpp",
     ],
     static_libs: [
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
+        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
+        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V1-cpp",
+        "android.hardware.power-V2-cpp",
+        "libaidlcommonsupport",
         "libcompositionengine_mocks",
         "libcompositionengine",
         "libframetimeline",
@@ -127,12 +139,14 @@
         "libgui_mocks",
         "liblayers_proto",
         "libperfetto_client_experimental",
-        "librenderengine_mocks",
         "librenderengine",
+        "librenderengine_mocks",
+        "libscheduler",
         "libserviceutils",
         "libtimestats",
         "libtimestats_atoms_proto",
         "libtimestats_proto",
+        "libtonemap",
         "libtrace_proto",
         "perfetto_trace_protos",
     ],
@@ -145,6 +159,7 @@
         "android.hardware.graphics.common@1.2",
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libcutils",
         "libEGL",
         "libfmq",
@@ -161,12 +176,14 @@
         "libsync",
         "libui",
         "libutils",
+        "server_configurable_flags",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
         "android.hardware.graphics.composer@2.4-command-buffer",
+        "android.hardware.graphics.composer3-command-buffer",
         "libsurfaceflinger_headers",
     ],
 }
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 6a7ec9b..6f85498 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
 #undef LOG_TAG
 #define LOG_TAG "CachingTest"
 
@@ -42,7 +37,7 @@
     sp<IBinder> binder = new BBinder();
     // test getting invalid client_cache_id
     client_cache_t id;
-    uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+    int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
     EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
 }
 
@@ -51,7 +46,7 @@
     client_cache_t id;
     id.token = binder;
     id.id = 0;
-    uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+    int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
     EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
 
     client_cache_t idB;
@@ -72,31 +67,28 @@
     std::vector<client_cache_t> ids;
     uint32_t cacheId = 0;
     // fill up cache
-    for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
         client_cache_t id;
         id.token = binder;
         id.id = cacheId;
         ids.push_back(id);
 
-        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+        int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
         cacheId++;
     }
-    for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(ids[i]);
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
     }
 
-    for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
         client_cache_t id;
         id.token = binder;
         id.id = cacheId;
-        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+        int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
         cacheId++;
     }
 }
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 52a36a2..6c96d5f 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -45,7 +45,6 @@
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
 #include "mock/MockTimeStats.h"
 #include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
@@ -77,17 +76,22 @@
 constexpr hal::HWLayerId HWC_LAYER = 5000;
 constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
 
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42);
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
 constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
 
 constexpr int DEFAULT_TEXTURE_ID = 6000;
-constexpr int DEFAULT_LAYER_STACK = 7000;
+constexpr ui::LayerStack LAYER_STACK{7000u};
 
 constexpr int DEFAULT_DISPLAY_MAX_LUMINANCE = 500;
 
 constexpr int DEFAULT_SIDEBAND_STREAM = 51;
 
+MATCHER(IsIdentityMatrix, "") {
+    constexpr auto kIdentity = mat4();
+    return (mat4(arg) == kIdentity);
+}
+
 class CompositionTest : public testing::Test {
 public:
     CompositionTest() {
@@ -95,7 +99,6 @@
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-        mFlinger.mutableEventQueue().reset(mMessageQueue);
         setupScheduler();
 
         EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
@@ -140,14 +143,11 @@
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
         EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
 
-        constexpr ISchedulerCallback* kCallback = nullptr;
+        constexpr scheduler::ISchedulerCallback* kCallback = nullptr;
         constexpr bool kHasMultipleConfigs = true;
         mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
                                 std::move(eventThread), std::move(sfEventThread), kCallback,
                                 kHasMultipleConfigs);
-
-        // Layer history should be created if there are multiple configs.
-        ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
     }
 
     void setupForceGeometryDirty() {
@@ -157,7 +157,7 @@
         // pain)
         // mFlinger.mutableVisibleRegionsDirty() = true;
 
-        mFlinger.mutableGeometryInvalid() = true;
+        mFlinger.mutableGeometryDirty() = true;
     }
 
     template <typename Case>
@@ -186,7 +186,6 @@
     Hwc2::mock::Composer* mComposer = nullptr;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     mock::TimeStats* mTimeStats = new mock::TimeStats();
-    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     Hwc2::mock::PowerAdvisor mPowerAdvisor;
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -202,8 +201,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
-    mFlinger.onMessageReceived(MessageQueue::REFRESH);
+    mFlinger.commitAndComposite();
 
     LayerCase::cleanup(this);
 }
@@ -215,8 +213,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
-    mFlinger.onMessageReceived(MessageQueue::REFRESH);
+    mFlinger.commitAndComposite();
 
     LayerCase::cleanup(this);
 }
@@ -247,14 +244,28 @@
                                                              "screenshot"),
                                            *mRenderEngine, true);
 
-    status_t result =
-            mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
-                                            forSystem, regionSampling);
-    EXPECT_EQ(NO_ERROR, result);
+    auto result = mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
+                                                  forSystem, regionSampling);
+    EXPECT_TRUE(result.valid());
+
+    auto& [status, drawFence] = result.get();
+
+    EXPECT_EQ(NO_ERROR, status);
+    if (drawFence.ok()) {
+        sync_wait(drawFence.get(), -1);
+    }
 
     LayerCase::cleanup(this);
 }
 
+template <class T>
+std::future<T> futureOf(T obj) {
+    std::promise<T> resultPromise;
+    std::future<T> resultFuture = resultPromise.get_future();
+    resultPromise.set_value(std::move(obj));
+    return resultFuture;
+}
+
 /* ------------------------------------------------------------------------
  * Variants for each display configuration which can be tested
  */
@@ -287,10 +298,8 @@
 
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setId(DEFAULT_DISPLAY_ID)
-                                     .setConnectionType(ui::DisplayConnectionType::Internal)
                                      .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
                                      .setIsSecure(Derived::IS_SECURE)
-                                     .setLayerStackId(DEFAULT_LAYER_STACK)
                                      .setPowerAdvisor(&test->mPowerAdvisor)
                                      .setName(std::string("Injected display for ") +
                                               test_info->test_case_name() + "." + test_info->name())
@@ -309,21 +318,21 @@
                                  .setPowerMode(Derived::INIT_POWER_MODE)
                                  .inject();
         Mock::VerifyAndClear(test->mNativeWindow);
-        test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
+        test->mDisplay->setLayerStack(LAYER_STACK);
     }
 
     template <typename Case>
     static void setupPreconditionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+                .WillOnce(DoAll(SetArgPointee<1>(
+                                        std::vector<aidl::android::hardware::graphics::composer3::
+                                                            DisplayCapability>({})),
                                 Return(Error::NONE)));
     }
 
     template <typename Case>
     static void setupCommonCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
-                .Times(1);
+        EXPECT_CALL(*test->mComposer, setColorTransform(HWC_DISPLAY, IsIdentityMatrix())).Times(1);
         EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1);
         EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1);
         EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
@@ -339,16 +348,18 @@
     template <typename Case>
     static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
-                                   const std::vector<const renderengine::LayerSettings*>&,
-                                   const std::shared_ptr<renderengine::ExternalTexture>&,
-                                   const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
+                                    const std::vector<renderengine::LayerSettings>&,
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    const bool, base::unique_fd&&)
+                                        -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.clip);
-                    return NO_ERROR;
+                    return futureOf<renderengine::RenderEngineResult>(
+                            {NO_ERROR, base::unique_fd()});
                 });
     }
 
@@ -361,24 +372,26 @@
     }
 
     static void setupHwcCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+                .Times(1);
 
         EXPECT_CALL(*test->mDisplaySurface,
-                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
+                    prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
                 .Times(1);
     }
 
     static void setupHwcClientCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+                .Times(1);
     }
 
     static void setupHwcForcedClientCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _)).Times(1);
+        EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _, _)).Times(1);
     }
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mDisplaySurface,
-                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU))
+                    prepareFrame(compositionengine::DisplaySurface::CompositionType::Gpu))
                 .Times(1);
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -388,17 +401,19 @@
                 .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
                                 Return(0)));
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
-                                   const std::vector<const renderengine::LayerSettings*>&,
-                                   const std::shared_ptr<renderengine::ExternalTexture>&,
-                                   const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
+                                    const std::vector<renderengine::LayerSettings>&,
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    const bool, base::unique_fd&&)
+                                        -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.clip);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
-                    return NO_ERROR;
+                    return futureOf<renderengine::RenderEngineResult>(
+                            {NO_ERROR, base::unique_fd()});
                 });
     }
 
@@ -438,9 +453,7 @@
     template <typename Case>
     static void setupCommonCompositionCallExpectations(CompositionTest* test) {
         // TODO: This seems like an unnecessary call if display is powered off.
-        EXPECT_CALL(*test->mComposer,
-                    setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
-                .Times(1);
+        EXPECT_CALL(*test->mComposer, setColorTransform(HWC_DISPLAY, IsIdentityMatrix())).Times(1);
 
         // TODO: This seems like an unnecessary call if display is powered off.
         Case::CompositionType::setupHwcSetCallExpectations(test);
@@ -521,15 +534,16 @@
 
     static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
         // TODO: Eliminate the complexity of actually creating a buffer
+        layer->setSizeForTest(LayerProperties::WIDTH, LayerProperties::HEIGHT);
         status_t err =
                 layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                                   LayerProperties::FORMAT);
         ASSERT_EQ(NO_ERROR, err);
         Mock::VerifyAndClear(test->mRenderEngine);
 
-        EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
         enqueueBuffer(test, layer);
-        Mock::VerifyAndClearExpectations(test->mMessageQueue);
+        Mock::VerifyAndClearExpectations(test->mFlinger.scheduler());
 
         bool ignoredRecomputeVisibleRegions;
         layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
@@ -607,7 +621,8 @@
             // TODO: use COLOR
             EXPECT_CALL(*test->mComposer,
                         setLayerColor(HWC_DISPLAY, HWC_LAYER,
-                                      IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
+                                      aidl::android::hardware::graphics::composer3::Color(
+                                              {1.0f, 1.0f, 1.0f, 1.0f})))
                     .Times(1);
         }
     }
@@ -622,10 +637,10 @@
 
     static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                             base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+                              const std::vector<renderengine::LayerSettings>& layerSettings,
+                              const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                              base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -633,23 +648,26 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so gtet the back layer.
+                    std::future<renderengine::RenderEngineResult> resultFuture =
+                            futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupREBufferCompositionCommonCallExpectations "
                                          "verification lambda";
-                        return NO_ERROR;
+                        return resultFuture;
                     }
-                    const renderengine::LayerSettings* layer = layerSettings.back();
-                    EXPECT_THAT(layer->source.buffer.buffer, Not(IsNull()));
-                    EXPECT_THAT(layer->source.buffer.fence, Not(IsNull()));
-                    EXPECT_EQ(DEFAULT_TEXTURE_ID, layer->source.buffer.textureName);
-                    EXPECT_EQ(false, layer->source.buffer.isY410BT2020);
-                    EXPECT_EQ(true, layer->source.buffer.usePremultipliedAlpha);
-                    EXPECT_EQ(false, layer->source.buffer.isOpaque);
-                    EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
-                    EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
-                    return NO_ERROR;
+                    const renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
+                    EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
+                    EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
+                    EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
+                    EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
+                    EXPECT_EQ(false, layer.source.buffer.isOpaque);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                    return resultFuture;
                 });
     }
 
@@ -671,10 +689,10 @@
 
     static void setupREColorCompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                             base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+                              const std::vector<renderengine::LayerSettings>& layerSettings,
+                              const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                              base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -682,21 +700,24 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
+                    std::future<renderengine::RenderEngineResult> resultFuture =
+                            futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
                     if (layerSettings.empty()) {
                         ADD_FAILURE()
                                 << "layerSettings was not expected to be empty in "
                                    "setupREColorCompositionCallExpectations verification lambda";
-                        return NO_ERROR;
+                        return resultFuture;
                     }
-                    const renderengine::LayerSettings* layer = layerSettings.back();
-                    EXPECT_THAT(layer->source.buffer.buffer, IsNull());
+                    const renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
                     EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                     LayerProperties::COLOR[2]),
-                              layer->source.solidColor);
-                    EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
-                    EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
-                    return NO_ERROR;
+                              layer.source.solidColor);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                    return resultFuture;
                 });
     }
 
@@ -748,10 +769,10 @@
 
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                             base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+                              const std::vector<renderengine::LayerSettings>& layerSettings,
+                              const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                              base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -759,19 +780,22 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
+                    std::future<renderengine::RenderEngineResult> resultFuture =
+                            futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupInsecureREBufferCompositionCommonCallExpectations "
                                          "verification lambda";
-                        return NO_ERROR;
+                        return resultFuture;
                     }
-                    const renderengine::LayerSettings* layer = layerSettings.back();
-                    EXPECT_THAT(layer->source.buffer.buffer, IsNull());
-                    EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer->source.solidColor);
-                    EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
-                    EXPECT_EQ(1.0f, layer->alpha);
-                    return NO_ERROR;
+                    const renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+                    EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(1.0f, layer.alpha);
+                    return resultFuture;
                 });
     }
 
@@ -815,16 +839,16 @@
 struct BaseLayerVariant {
     template <typename L, typename F>
     static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
-        EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(0);
+        EXPECT_CALL(*test->mFlinger.scheduler(), postMessage(_)).Times(0);
 
         sp<L> layer = factory();
 
         // Layer should be registered with scheduler.
-        EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+        EXPECT_EQ(1u, test->mFlinger.scheduler()->layerHistorySize());
 
         Mock::VerifyAndClear(test->mComposer);
         Mock::VerifyAndClear(test->mRenderEngine);
-        Mock::VerifyAndClearExpectations(test->mMessageQueue);
+        Mock::VerifyAndClearExpectations(test->mFlinger.scheduler());
 
         initLayerDrawingStateAndComputeBounds(test, layer);
 
@@ -834,7 +858,7 @@
     template <typename L>
     static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
+        layerDrawingState.layerStack = LAYER_STACK;
         layerDrawingState.width = 100;
         layerDrawingState.height = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
@@ -864,8 +888,8 @@
         test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
 
         // Layer should be unregistered with scheduler.
-        test->mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
-        EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
+        test->mFlinger.commit();
+        EXPECT_EQ(0u, test->mFlinger.scheduler()->layerHistorySize());
     }
 };
 
@@ -878,7 +902,6 @@
         FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
             return new EffectLayer(
                     LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
-                                      LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                       LayerProperties::LAYER_FLAGS, LayerMetadata()));
         });
 
@@ -917,7 +940,6 @@
         FlingerLayerType layer =
                 Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
                     LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
-                                           LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                            LayerProperties::LAYER_FLAGS, LayerMetadata());
                     args.textureName = test->mFlinger.mutableTexturePool().back();
                     return new BufferQueueLayer(args);
@@ -929,7 +951,6 @@
     }
 
     static void cleanupInjectedLayers(CompositionTest* test) {
-        EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(1);
         Base::cleanupInjectedLayers(test);
     }
 
@@ -967,7 +988,6 @@
 
     static FlingerLayerType createLayer(CompositionTest* test) {
         LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
-                               LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                LayerProperties::LAYER_FLAGS, LayerMetadata());
         FlingerLayerType layer = new ContainerLayer(args);
         Base::template initLayerDrawingStateAndComputeBounds(test, layer);
@@ -1015,9 +1035,10 @@
     }
 };
 
-template <IComposerClient::Composition CompositionType>
+template <aidl::android::hardware::graphics::composer3::Composition CompositionType>
 struct KeepCompositionTypeVariant {
-    static constexpr hal::Composition TYPE = CompositionType;
+    static constexpr aidl::android::hardware::graphics::composer3::Composition TYPE =
+            CompositionType;
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
         if (!test->mDisplayOff) {
@@ -1032,10 +1053,11 @@
     }
 };
 
-template <IComposerClient::Composition InitialCompositionType,
-          IComposerClient::Composition FinalCompositionType>
+template <aidl::android::hardware::graphics::composer3::Composition InitialCompositionType,
+          aidl::android::hardware::graphics::composer3::Composition FinalCompositionType>
 struct ChangeCompositionTypeVariant {
-    static constexpr hal::Composition TYPE = FinalCompositionType;
+    static constexpr aidl::android::hardware::graphics::composer3::Composition TYPE =
+            FinalCompositionType;
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
         if (!test->mDisplayOff) {
@@ -1049,8 +1071,9 @@
         EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _))
                 .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::Layer>{
                                         static_cast<Hwc2::Layer>(HWC_LAYER)}),
-                                SetArgPointee<2>(std::vector<IComposerClient::Composition>{
-                                        FinalCompositionType}),
+                                SetArgPointee<2>(
+                                        std::vector<aidl::android::hardware::graphics::composer3::
+                                                            Composition>{FinalCompositionType}),
                                 Return(Error::NONE)));
     }
 };
@@ -1244,25 +1267,28 @@
  */
 
 TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, REComposedNormalBufferLayer) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-                            ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
-                                                         IComposerClient::Composition::CLIENT>,
-                            RECompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            ChangeCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            RECompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, captureScreenNormalBufferLayer) {
@@ -1276,25 +1302,28 @@
  */
 
 TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, REComposedEffectLayer) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
-                            ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR,
-                                                         IComposerClient::Composition::CLIENT>,
-                            RECompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+            ChangeCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR,
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            RECompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, captureScreenEffectLayer) {
@@ -1308,25 +1337,28 @@
  */
 
 TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::SIDEBAND>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::SIDEBAND>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, REComposedSidebandBufferLayer) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
-                            ChangeCompositionTypeVariant<IComposerClient::Composition::SIDEBAND,
-                                                         IComposerClient::Composition::CLIENT>,
-                            RECompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+            ChangeCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::SIDEBAND,
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            RECompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, captureScreenSidebandBufferLayer) {
@@ -1340,25 +1372,28 @@
  */
 
 TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, REComposedSecureBufferLayer) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
-                            ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
-                                                         IComposerClient::Composition::CLIENT>,
-                            RECompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+            ChangeCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            RECompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, captureScreenSecureBufferLayerOnSecureDisplay) {
@@ -1372,17 +1407,19 @@
  */
 
 TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
-                            ForcedClientCompositionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            ForcedClientCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
-                            ForcedClientCompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            ForcedClientCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) {
@@ -1397,22 +1434,24 @@
 
 TEST_F(CompositionTest,
        HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<InsecureDisplaySetupVariant,
-                            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
-                                              ContainerLayerVariant<SecureLayerProperties>>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
-                            ForcedClientCompositionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            InsecureDisplaySetupVariant,
+            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+                              ContainerLayerVariant<SecureLayerProperties>>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            ForcedClientCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest,
        HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<InsecureDisplaySetupVariant,
-                            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
-                                              ContainerLayerVariant<SecureLayerProperties>>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
-                            ForcedClientCompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            InsecureDisplaySetupVariant,
+            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+                              ContainerLayerVariant<SecureLayerProperties>>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            ForcedClientCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
@@ -1428,25 +1467,28 @@
  */
 
 TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CURSOR>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
-                            HwcCompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CURSOR>,
+            HwcCompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, REComposedCursorLayer) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
-                            ChangeCompositionTypeVariant<IComposerClient::Composition::CURSOR,
-                                                         IComposerClient::Composition::CLIENT>,
-                            RECompositionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+            ChangeCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CURSOR,
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            RECompositionResultVariant>>();
 }
 
 TEST_F(CompositionTest, captureScreenCursorLayer) {
@@ -1463,7 +1505,8 @@
     mDisplayOff = true;
     displayRefreshCompositionDirtyGeometry<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
             HwcCompositionResultVariant>>();
 }
 
@@ -1471,7 +1514,8 @@
     mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
             HwcCompositionResultVariant>>();
 }
 
@@ -1479,8 +1523,9 @@
     mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-            ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
-                                         IComposerClient::Composition::CLIENT>,
+            ChangeCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
             RECompositionResultVariant>>();
 }
 
@@ -1495,17 +1540,19 @@
  */
 
 TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
-                            ForcedClientCompositionViaDebugOptionResultVariant>>();
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            ForcedClientCompositionViaDebugOptionResultVariant>>();
 }
 
 TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
-                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
-                            ForcedClientCompositionViaDebugOptionResultVariant>>();
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            KeepCompositionTypeVariant<
+                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+            ForcedClientCompositionViaDebugOptionResultVariant>>();
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index d4cfbbb..5a0033e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -29,7 +29,7 @@
 
 class InitiateModeChangeTest : public DisplayTransactionTest {
 public:
-    using Event = scheduler::RefreshRateConfigEvent;
+    using Event = scheduler::DisplayModeEvent;
 
     void SetUp() override {
         injectFakeBufferQueueFactory();
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
new file mode 100644
index 0000000..73c60e1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class SetDisplayBrightnessTest : public DisplayTransactionTest {
+public:
+    sp<DisplayDevice> getDisplayDevice() { return injectDefaultInternalDisplay({}); }
+};
+
+TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessNoComposite) {
+    MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = 0.5f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(false);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
+TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithComposite) {
+    MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = 0.5f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(kDisplayBrightness,
+              displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
+TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithCompositeShortCircuitsOnNoOp) {
+    MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = 0.5f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(kDisplayBrightness,
+              displayDevice->getCompositionDisplay()->getState().displayBrightness);
+    displayDevice->getCompositionDisplay()->editState().displayBrightness = std::nullopt;
+
+    displayDevice->stageBrightness(kDisplayBrightness);
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
index 9fe30f8..3d24ecb 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -84,10 +84,10 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+        EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -96,13 +96,14 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
         // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
         // size width and height swapped
         EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
-                  compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+                  compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+                  compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -111,9 +112,9 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+        EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -122,13 +123,14 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
         // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
         // size width and height swapped
         EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
-                  compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+                  compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+                  compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index cc24323..c318e28 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -38,7 +38,6 @@
 
     // Default to no wide color display support configured
     mFlinger.mutableHasWideColorDisplay() = false;
-    mFlinger.mutableUseColorManagement() = false;
     mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
 
     mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) {
@@ -51,7 +50,6 @@
     });
 
     injectMockScheduler();
-    mFlinger.mutableEventQueue().reset(mMessageQueue);
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     mFlinger.mutableInterceptor() = mSurfaceInterceptor;
 
@@ -62,6 +60,7 @@
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    mFlinger.resetScheduler(nullptr);
 }
 
 void DisplayTransactionTest::injectMockScheduler() {
@@ -122,7 +121,7 @@
 
 sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
         std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
-    constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777);
+    constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
     constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
     constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
     constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
@@ -139,20 +138,18 @@
     EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
     EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
 
-    constexpr auto kConnectionType = ui::DisplayConnectionType::Internal;
-    constexpr bool kIsPrimary = true;
-
     auto compositionDisplay =
             compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
                                                    compositionengine::DisplayCreationArgsBuilder()
                                                            .setId(DEFAULT_DISPLAY_ID)
-                                                           .setConnectionType(kConnectionType)
                                                            .setPixels({DEFAULT_DISPLAY_WIDTH,
                                                                        DEFAULT_DISPLAY_HEIGHT})
                                                            .setPowerAdvisor(&mPowerAdvisor)
                                                            .build());
 
-    auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, kConnectionType,
+    constexpr bool kIsPrimary = true;
+    auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+                                              ui::DisplayConnectionType::Internal,
                                               DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
 
     injector.setNativeWindow(mNativeWindow);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index de058a4..69ac26e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -47,7 +47,6 @@
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
 #include "mock/MockNativeWindowSurface.h"
 #include "mock/MockSchedulerCallback.h"
 #include "mock/MockSurfaceInterceptor.h"
@@ -67,9 +66,9 @@
 using testing::Return;
 using testing::SetArgPointee;
 
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
 using hal::ColorMode;
 using hal::Connection;
-using hal::DisplayCapability;
 using hal::DisplayType;
 using hal::Error;
 using hal::Hdr;
@@ -118,12 +117,11 @@
     // to keep a reference to them for use in setting up call expectations.
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     Hwc2::mock::Composer* mComposer = nullptr;
-    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
 
     mock::VsyncController* mVsyncController = new mock::VsyncController;
     mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
-    mock::SchedulerCallback mSchedulerCallback;
+    scheduler::mock::SchedulerCallback mSchedulerCallback;
     mock::EventThread* mEventThread = new mock::EventThread;
     mock::EventThread* mSFEventThread = new mock::EventThread;
 
@@ -233,7 +231,7 @@
 //     2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
 //     3) GpuVirtualDisplayIdType for virtual display without HWC backing.
 template <typename DisplayIdType, int width, int height, Critical critical, Async async,
-          Secure secure, Primary primary, int grallocUsage>
+          Secure secure, Primary primary, int grallocUsage, int displayFlags>
 struct DisplayVariant {
     using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
     using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
@@ -261,17 +259,14 @@
     // Whether the display is primary
     static constexpr Primary PRIMARY = primary;
 
+    static constexpr int DISPLAY_FLAGS = displayFlags;
+
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
         ceDisplayArgs.setId(DISPLAY_ID::get())
                 .setPixels({WIDTH, HEIGHT})
                 .setPowerAdvisor(&test->mPowerAdvisor);
 
-        const auto connectionType = CONNECTION_TYPE::value;
-        if (connectionType) {
-            ceDisplayArgs.setConnectionType(*connectionType);
-        }
-
         auto compositionDisplay =
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs.build());
@@ -279,7 +274,7 @@
         auto injector =
                 TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
                                                                   compositionDisplay,
-                                                                  connectionType,
+                                                                  CONNECTION_TYPE::value,
                                                                   HWC_DISPLAY_ID_OPT::value,
                                                                   static_cast<bool>(PRIMARY));
 
@@ -388,7 +383,6 @@
 
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setId(DisplayVariant::DISPLAY_ID::get())
-                                     .setConnectionType(PhysicalDisplay::CONNECTION_TYPE)
                                      .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
                                      .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
@@ -475,16 +469,19 @@
 constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
         GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
 
+constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
+
 template <typename PhysicalDisplay, int width, int height, Critical critical>
 struct PhysicalDisplayVariant
       : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
                        Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
-                       GRALLOC_USAGE_PHYSICAL_DISPLAY>,
-        HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
-                          DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
-                                         critical, Async::FALSE, Secure::TRUE,
-                                         PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
-                          PhysicalDisplay> {};
+                       GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+        HwcDisplayVariant<
+                PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+                DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+                               Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+                               GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+                PhysicalDisplay> {};
 
 template <bool hasIdentificationData>
 struct PrimaryDisplay {
@@ -526,13 +523,16 @@
 // A virtual display not supported by the HWC.
 constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
 
+constexpr int VIRTUAL_DISPLAY_FLAGS = 0x0;
+
 template <int width, int height, Secure secure>
 struct NonHwcVirtualDisplayVariant
       : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
-                       Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
-    using Base =
-            DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
-                           secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+                       Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY,
+                       VIRTUAL_DISPLAY_FLAGS> {
+    using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE,
+                                Async::TRUE, secure, Primary::FALSE,
+                                GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>;
 
     static void injectHwcDisplay(DisplayTransactionTest*) {}
 
@@ -575,13 +575,16 @@
 template <int width, int height, Secure secure>
 struct HwcVirtualDisplayVariant
       : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
-                       secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
-        HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
-                          DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
-                                         Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
-                                         GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+                       secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY,
+                       VIRTUAL_DISPLAY_FLAGS>,
+        HwcDisplayVariant<
+                HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+                DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
+                               Async::TRUE, secure, Primary::FALSE,
+                               GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>> {
     using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
-                                Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+                                Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER,
+                                VIRTUAL_DISPLAY_FLAGS>;
     using Self = HwcVirtualDisplayVariant<width, height, secure>;
 
     static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
@@ -671,7 +674,6 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = false;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableUseColorManagement() = true;
         test->mFlinger.mutableHasWideColorDisplay() = true;
     }
 
@@ -690,7 +692,6 @@
 
     static void injectConfigChange(DisplayTransactionTest* test) {
         test->mFlinger.mutableHasWideColorDisplay() = false;
-        test->mFlinger.mutableUseColorManagement() = false;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 4ff7592..67a0d7e 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -28,6 +28,7 @@
 
 #include "AsyncCallRecorder.h"
 #include "DisplayHardware/DisplayMode.h"
+#include "FrameTimeline.h"
 #include "Scheduler/EventThread.h"
 
 using namespace std::chrono_literals;
@@ -41,10 +42,13 @@
 
 namespace {
 
-constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
-constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
-constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(111u);
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(222u);
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT =
+        PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu);
+
 constexpr std::chrono::duration VSYNC_PERIOD(16ms);
+
 class MockVSyncSource : public VSyncSource {
 public:
     const char* getName() const override { return "test"; }
@@ -93,6 +97,8 @@
                                               ConnectionEventRecorder& connectionEventRecorder,
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
     void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
+    void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+                                               nsecs_t preferredDeadline);
     void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                 bool expectedConnected);
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -117,6 +123,7 @@
     std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
     sp<MockEventThreadConnection> mThrottledConnection;
+    std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
 
     static constexpr uid_t mConnectionUid = 443;
     static constexpr uid_t mThrottledConnectionUid = 177;
@@ -170,8 +177,8 @@
         return VSYNC_PERIOD.count();
     };
 
-    mThread = std::make_unique<impl::EventThread>(std::move(source),
-                                                  /*tokenManager=*/nullptr,
+    mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
+    mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
                                                   mInterceptVSyncCallRecorder.getInvocable(),
                                                   throttleVsync, getVsyncPeriod);
 
@@ -244,6 +251,45 @@
                                          expectedCount);
 }
 
+void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+                                                            nsecs_t preferredDeadline) {
+    auto args = mConnectionEventCallRecorder.waitForCall();
+    ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+                                  << expectedTimestamp;
+    const auto& event = std::get<0>(args.value());
+    for (int i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+        auto prediction =
+                mTokenManager->getPredictionsForToken(event.vsync.frameTimelines[i].vsyncId);
+        EXPECT_TRUE(prediction.has_value());
+        EXPECT_EQ(prediction.value().endTime, event.vsync.frameTimelines[i].deadlineTimestamp)
+                << "Deadline timestamp does not match cached value";
+        EXPECT_EQ(prediction.value().presentTime,
+                  event.vsync.frameTimelines[i].expectedVSyncTimestamp)
+                << "Expected vsync timestamp does not match cached value";
+
+        if (i > 0) {
+            EXPECT_GT(event.vsync.frameTimelines[i].deadlineTimestamp,
+                      event.vsync.frameTimelines[i - 1].deadlineTimestamp)
+                    << "Deadline timestamp out of order for frame timeline " << i;
+            EXPECT_GT(event.vsync.frameTimelines[i].expectedVSyncTimestamp,
+                      event.vsync.frameTimelines[i - 1].expectedVSyncTimestamp)
+                    << "Expected vsync timestamp out of order for frame timeline " << i;
+        }
+        if (event.vsync.frameTimelines[i].deadlineTimestamp == preferredDeadline) {
+            EXPECT_EQ(i, event.vsync.preferredFrameTimelineIndex)
+                    << "Preferred frame timeline index should be " << i;
+            // For the platform-preferred frame timeline, the vsync ID is 0 because the first frame
+            // timeline is made before the rest.
+            EXPECT_EQ(0, event.vsync.frameTimelines[i].vsyncId)
+                    << "Vsync ID incorrect for frame timeline " << i;
+        } else {
+            // Vsync ID 0 is used for the preferred frame timeline.
+            EXPECT_EQ(i + 1, event.vsync.frameTimelines[i].vsyncId)
+                    << "Vsync ID incorrect for frame timeline " << i;
+        }
+    }
+}
+
 void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                              bool expectedConnected) {
     auto args = mConnectionEventCallRecorder.waitForCall();
@@ -341,6 +387,19 @@
     expectVSyncSetEnabledCallReceived(false);
 }
 
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+    // Signal that we want the next vsync event to be posted to the connection
+    mThread->requestNextVsync(mConnection);
+
+    expectVSyncSetEnabledCallReceived(true);
+
+    // Use the received callback to signal a vsync event.
+    // The interceptor should receive the event, as well as the connection.
+    mCallback->onVSyncEvent(123, 456, 789);
+    expectInterceptCallReceived(123);
+    expectVsyncEventFrameTimelinesCorrect(123, 789);
+}
+
 TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
     // Create a first connection, register it, and request a vsync rate of zero.
     ConnectionEventRecorder firstConnectionEventRecorder{0};
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
new file mode 100644
index 0000000..0905cd1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#undef LOG_TAG
+#define LOG_TAG "FlagManagerTest"
+
+#include "FlagManager.h"
+
+#include <android-base/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+#include <optional>
+
+namespace android {
+
+using testing::Return;
+
+class MockFlagManager : public FlagManager {
+public:
+    MockFlagManager() = default;
+    ~MockFlagManager() = default;
+
+    MOCK_METHOD(std::string, getServerConfigurableFlag, (const std::string& experimentFlagName),
+                (const, override));
+};
+
+class FlagManagerTest : public testing::Test {
+public:
+    FlagManagerTest();
+    ~FlagManagerTest() override;
+    std::unique_ptr<MockFlagManager> mFlagManager;
+
+    template <typename T>
+    T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
+               T defaultValue);
+};
+
+FlagManagerTest::FlagManagerTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    mFlagManager = std::make_unique<MockFlagManager>();
+}
+
+FlagManagerTest::~FlagManagerTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+template <typename T>
+T FlagManagerTest::getValue(const std::string& experimentFlagName,
+                            std::optional<T> systemPropertyOpt, T defaultValue) {
+    return mFlagManager->getValue(experimentFlagName, systemPropertyOpt, defaultValue);
+}
+
+namespace {
+TEST_F(FlagManagerTest, getValue_bool_default) {
+    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
+    const bool defaultValue = false;
+    std::optional<bool> systemPropertyValue = std::nullopt;
+    const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, defaultValue);
+}
+
+TEST_F(FlagManagerTest, getValue_bool_sysprop) {
+    const bool defaultValue = false;
+    std::optional<bool> systemPropertyValue = std::make_optional(true);
+    const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, true);
+}
+
+TEST_F(FlagManagerTest, getValue_bool_experiment) {
+    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("1"));
+    const bool defaultValue = false;
+    std::optional<bool> systemPropertyValue = std::nullopt;
+    const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, true);
+}
+
+TEST_F(FlagManagerTest, getValue_int32_default) {
+    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
+    int32_t defaultValue = 30;
+    std::optional<int32_t> systemPropertyValue = std::nullopt;
+    int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, defaultValue);
+}
+
+TEST_F(FlagManagerTest, getValue_int32_sysprop) {
+    int32_t defaultValue = 30;
+    std::optional<int32_t> systemPropertyValue = std::make_optional(10);
+    int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, 10);
+}
+
+TEST_F(FlagManagerTest, getValue_int32_experiment) {
+    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
+    std::int32_t defaultValue = 30;
+    std::optional<std::int32_t> systemPropertyValue = std::nullopt;
+    std::int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, 50);
+}
+
+TEST_F(FlagManagerTest, getValue_int64_default) {
+    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
+    int64_t defaultValue = 30;
+    std::optional<int64_t> systemPropertyValue = std::nullopt;
+    int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, defaultValue);
+}
+
+TEST_F(FlagManagerTest, getValue_int64_sysprop) {
+    int64_t defaultValue = 30;
+    std::optional<int64_t> systemPropertyValue = std::make_optional(10);
+    int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, 10);
+}
+
+TEST_F(FlagManagerTest, getValue_int64_experiment) {
+    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
+    int64_t defaultValue = 30;
+    std::optional<int64_t> systemPropertyValue = std::nullopt;
+    int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
+    ASSERT_EQ(result, 50);
+}
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FpsOps.h b/services/surfaceflinger/tests/unittests/FpsOps.h
new file mode 100644
index 0000000..7c737dc
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsOps.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <scheduler/Fps.h>
+
+namespace android {
+
+// Pull Fps operators into its namespace to enable ADL for EXPECT_EQ, EXPECT_LT, etc.
+
+inline bool operator==(Fps lhs, Fps rhs) {
+    return fps_approx_ops::operator==(lhs, rhs);
+}
+
+inline bool operator<(Fps lhs, Fps rhs) {
+    return fps_approx_ops::operator<(lhs, rhs);
+}
+
+inline bool operator!=(Fps lhs, Fps rhs) {
+    return fps_approx_ops::operator!=(lhs, rhs);
+}
+
+inline bool operator>(Fps lhs, Fps rhs) {
+    return fps_approx_ops::operator>(lhs, rhs);
+}
+
+inline bool operator<=(Fps lhs, Fps rhs) {
+    return fps_approx_ops::operator<=(lhs, rhs);
+}
+
+inline bool operator>=(Fps lhs, Fps rhs) {
+    return fps_approx_ops::operator>=(lhs, rhs);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index 010c675..bb1f432 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -17,6 +17,8 @@
 #undef LOG_TAG
 #define LOG_TAG "FpsReporterTest"
 
+#include <chrono>
+
 #include <android/gui/BnFpsListener.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -36,6 +38,8 @@
 
 namespace android {
 
+using namespace std::chrono_literals;
+
 using testing::_;
 using testing::DoAll;
 using testing::Mock;
@@ -114,8 +118,7 @@
 
 sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
-    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
-                           LAYER_FLAGS, metadata);
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
     return new BufferStateLayer(args);
 }
 
diff --git a/services/surfaceflinger/tests/unittests/FpsTest.cpp b/services/surfaceflinger/tests/unittests/FpsTest.cpp
index db732cf..88b74d2 100644
--- a/services/surfaceflinger/tests/unittests/FpsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsTest.cpp
@@ -14,85 +14,58 @@
  * limitations under the License.
  */
 
-#include "Fps.h"
-
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <scheduler/Fps.h>
+
+#include "FpsOps.h"
+
 namespace android {
 
 TEST(FpsTest, construct) {
-    Fps fpsDefault;
-    EXPECT_FALSE(fpsDefault.isValid());
+    EXPECT_FALSE(Fps().isValid());
 
-    Fps fps1(60.0f);
-    EXPECT_TRUE(fps1.isValid());
-    Fps fps2 = Fps::fromPeriodNsecs(static_cast<nsecs_t>(1e9f / 60.0f));
-    EXPECT_TRUE(fps2.isValid());
-    EXPECT_TRUE(fps1.equalsWithMargin(fps2));
+    EXPECT_FALSE((0_Hz).isValid());
+    EXPECT_TRUE((120_Hz).isValid());
+    EXPECT_TRUE((0.5_Hz).isValid());
+
+    EXPECT_FALSE(Fps::fromPeriodNsecs(0).isValid());
+
+    const Fps fps = Fps::fromPeriodNsecs(16'666'667);
+    EXPECT_TRUE(fps.isValid());
+    EXPECT_EQ(fps, 60_Hz);
 }
 
 TEST(FpsTest, compare) {
-    constexpr float kEpsilon = 1e-4f;
-    const Fps::EqualsInBuckets equalsInBuckets;
-    const Fps::EqualsWithMargin equalsWithMargin;
+    EXPECT_EQ(60_Hz, 60_Hz);
+    EXPECT_EQ(60_Hz, 59.9999_Hz);
+    EXPECT_EQ(60_Hz, 60.0001_Hz);
 
-    EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f)));
-    EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f - kEpsilon)));
-    EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f + kEpsilon)));
+    EXPECT_LE(60_Hz, 60_Hz);
+    EXPECT_LE(60_Hz, 59.9999_Hz);
+    EXPECT_LE(60_Hz, 60.0001_Hz);
 
-    EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f)));
-    EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f - kEpsilon)));
-    EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f + kEpsilon)));
+    EXPECT_GE(60_Hz, 60_Hz);
+    EXPECT_GE(60_Hz, 59.9999_Hz);
+    EXPECT_GE(60_Hz, 60.0001_Hz);
 
-    EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f)));
-    EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f - kEpsilon)));
-    EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f + kEpsilon)));
-
-    EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f + kEpsilon)));
-    EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f)));
-    EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f - kEpsilon)));
-
-    EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f + kEpsilon)));
-    EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f)));
-    EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f - kEpsilon)));
-
-    // Fps with difference of 1 should be different
-    EXPECT_FALSE(Fps(60.0f).equalsWithMargin(Fps(61.f)));
-    EXPECT_TRUE(Fps(60.0f).lessThanWithMargin(Fps(61.f)));
-    EXPECT_TRUE(Fps(60.0f).greaterThanWithMargin(Fps(59.f)));
+    // Fps with difference of 1 should be different.
+    EXPECT_NE(60_Hz, 61_Hz);
+    EXPECT_LT(60_Hz, 61_Hz);
+    EXPECT_GT(60_Hz, 59_Hz);
 
     // These are common refresh rates which should be different.
-    EXPECT_FALSE(Fps(60.0f).equalsWithMargin(Fps(59.94f)));
-    EXPECT_TRUE(Fps(60.0f).greaterThanWithMargin(Fps(59.94f)));
-    EXPECT_FALSE(equalsInBuckets(Fps(60.0f), Fps(59.94f)));
-    EXPECT_FALSE(equalsWithMargin(Fps(60.0f), Fps(59.94f)));
-    EXPECT_NE(std::hash<Fps>()(Fps(60.0f)), std::hash<Fps>()(Fps(59.94f)));
-
-    EXPECT_FALSE(Fps(30.0f).equalsWithMargin(Fps(29.97f)));
-    EXPECT_TRUE(Fps(30.0f).greaterThanWithMargin(Fps(29.97f)));
-    EXPECT_FALSE(equalsInBuckets(Fps(30.0f), Fps(29.97f)));
-    EXPECT_FALSE(equalsWithMargin(Fps(30.0f), Fps(29.97f)));
-    EXPECT_NE(std::hash<Fps>()(Fps(30.0f)), std::hash<Fps>()(Fps(29.97f)));
+    EXPECT_NE(60_Hz, 59.94_Hz);
+    EXPECT_GT(60_Hz, 59.94_Hz);
+    EXPECT_NE(30_Hz, 29.97_Hz);
+    EXPECT_GT(30_Hz, 29.97_Hz);
 }
 
 TEST(FpsTest, getIntValue) {
-    EXPECT_EQ(30, Fps(30.1f).getIntValue());
-    EXPECT_EQ(31, Fps(30.9f).getIntValue());
-    EXPECT_EQ(31, Fps(30.5f).getIntValue());
-}
-
-TEST(FpsTest, equalsInBucketsImpliesEqualHashes) {
-    constexpr float kStep = 1e-4f;
-    const Fps::EqualsInBuckets equals;
-    for (float fps = 30.0f; fps < 31.0f; fps += kStep) {
-        const Fps left(fps);
-        const Fps right(fps + kStep);
-        if (equals(left, right)) {
-            ASSERT_EQ(std::hash<Fps>()(left), std::hash<Fps>()(right))
-                    << "left= " << left << " right=" << right;
-        }
-    }
+    EXPECT_EQ(30, (30.1_Hz).getIntValue());
+    EXPECT_EQ(31, (30.9_Hz).getIntValue());
+    EXPECT_EQ(31, (30.5_Hz).getIntValue());
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 97b60e0..397c619 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -169,13 +169,14 @@
 
 static const std::string sLayerNameOne = "layer1";
 static const std::string sLayerNameTwo = "layer2";
-static constexpr const uid_t sUidOne = 0;
-static constexpr pid_t sPidOne = 10;
-static constexpr pid_t sPidTwo = 20;
-static constexpr int32_t sInputEventId = 5;
-static constexpr int32_t sLayerIdOne = 1;
-static constexpr int32_t sLayerIdTwo = 2;
-static constexpr int32_t sGameMode = 0;
+
+constexpr const uid_t sUidOne = 0;
+constexpr pid_t sPidOne = 10;
+constexpr pid_t sPidTwo = 20;
+constexpr int32_t sInputEventId = 5;
+constexpr int32_t sLayerIdOne = 1;
+constexpr int32_t sLayerIdTwo = 2;
+constexpr GameMode sGameMode = GameMode::Unsupported;
 
 TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
@@ -559,7 +560,7 @@
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
-    Fps refreshRate = Fps(11.0);
+    Fps refreshRate = 11_Hz;
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
                                                                 sLayerNameOne, sGameMode,
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
index 3fa1a2c..981ca1d 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -53,7 +53,7 @@
 
     sp<BufferStateLayer> createBufferStateLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
                                LayerMetadata());
         return new BufferStateLayer(args);
     }
@@ -91,8 +91,8 @@
     }
 
     // Mocks the behavior of applying a transaction from WMShell
-    void setGameModeMetadata(sp<Layer> layer, int gameMode) {
-        mLayerMetadata.setInt32(METADATA_GAME_MODE, gameMode);
+    void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) {
+        mLayerMetadata.setInt32(METADATA_GAME_MODE, static_cast<int32_t>(gameMode));
         layer->setMetadata(mLayerMetadata);
         layer->setGameModeForTree(gameMode);
     }
@@ -109,51 +109,52 @@
     sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
     rootLayer->addChild(childLayer1);
     rootLayer->addChild(childLayer2);
-    rootLayer->setGameModeForTree(/*gameMode*/ 2);
+    rootLayer->setGameModeForTree(GameMode::Performance);
 
-    EXPECT_EQ(rootLayer->getGameMode(), 2);
-    EXPECT_EQ(childLayer1->getGameMode(), 2);
-    EXPECT_EQ(childLayer2->getGameMode(), 2);
+    EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+    EXPECT_EQ(childLayer1->getGameMode(), GameMode::Performance);
+    EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
 }
 
 TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
     sp<BufferStateLayer> rootLayer = createBufferStateLayer();
     sp<BufferStateLayer> childLayer = createBufferStateLayer();
-    rootLayer->setGameModeForTree(/*gameMode*/ 2);
+    rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
-    EXPECT_EQ(rootLayer->getGameMode(), 2);
-    EXPECT_EQ(childLayer->getGameMode(), 2);
+    EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+    EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
 }
 
 TEST_F(GameModeTest, RemoveChildResetsGameMode) {
     sp<BufferStateLayer> rootLayer = createBufferStateLayer();
     sp<BufferStateLayer> childLayer = createBufferStateLayer();
-    rootLayer->setGameModeForTree(/*gameMode*/ 2);
+    rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
-    EXPECT_EQ(rootLayer->getGameMode(), 2);
-    EXPECT_EQ(childLayer->getGameMode(), 2);
+    EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+    EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
 
     rootLayer->removeChild(childLayer);
-    EXPECT_EQ(childLayer->getGameMode(), 0);
+    EXPECT_EQ(childLayer->getGameMode(), GameMode::Unsupported);
 }
 
 TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
     sp<BufferStateLayer> rootLayer = createBufferStateLayer();
     sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
     sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
-    rootLayer->setGameModeForTree(/*gameMode*/ 1);
+    rootLayer->setGameModeForTree(GameMode::Standard);
     rootLayer->addChild(childLayer1);
 
-    setGameModeMetadata(childLayer2, /*gameMode*/ 2);
+    setGameModeMetadata(childLayer2, GameMode::Performance);
     rootLayer->addChild(childLayer2);
 
-    EXPECT_EQ(rootLayer->getGameMode(), 1);
-    EXPECT_EQ(childLayer1->getGameMode(), 1);
-    EXPECT_EQ(childLayer2->getGameMode(), 2);
+    EXPECT_EQ(rootLayer->getGameMode(), GameMode::Standard);
+    EXPECT_EQ(childLayer1->getGameMode(), GameMode::Standard);
+    EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
 
     rootLayer->removeChild(childLayer2);
-    EXPECT_EQ(childLayer2->getGameMode(), 2);
+    EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
 }
-} // namespace android
\ No newline at end of file
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 655baf8..765dec3 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -36,6 +36,7 @@
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/Hal.h"
+#include "DisplayIdentificationTest.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockHWC2.h"
 
@@ -57,6 +58,29 @@
 using ::testing::SetArgPointee;
 using ::testing::StrictMock;
 
+TEST(HWComposerTest, isHeadless) {
+    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    ASSERT_TRUE(hwc.isHeadless());
+
+    const hal::HWDisplayId hwcId = 1;
+
+    EXPECT_CALL(*mHal, getDisplayIdentificationData(_, _, _))
+            .WillOnce(DoAll(SetArgPointee<2>(getExternalEdid()),
+                            Return(hardware::graphics::composer::V2_1::Error::NONE)));
+
+    EXPECT_CALL(*mHal, setVsyncEnabled(_, _));
+    EXPECT_CALL(*mHal, setClientTargetSlotCount(_));
+
+    auto info = hwc.onHotplug(hwcId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+    auto displayId = info->id;
+    ASSERT_FALSE(hwc.isHeadless());
+
+    hwc.disconnectDisplay(displayId);
+    ASSERT_TRUE(hwc.isHeadless());
+}
+
 struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
     MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
     MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
@@ -86,7 +110,8 @@
                             }),
                             Return(hardware::graphics::composer::V2_4::Error::NONE)));
     EXPECT_CALL(*mHal, registerCallback(_));
-    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+    EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+            .WillOnce(Return(false));
 
     impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
     hwc.setCallback(&mCallback);
@@ -104,7 +129,8 @@
     EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
             .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
     EXPECT_CALL(*mHal, registerCallback(_));
-    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+    EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+            .WillOnce(Return(false));
 
     impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
     hwc.setCallback(&mCallback);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 02ec7fc..cdb2240 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -26,6 +26,7 @@
 #include <gtest/gtest.h>
 #include <log/log.h>
 
+#include "FpsOps.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/LayerInfo.h"
 #include "TestableScheduler.h"
@@ -37,9 +38,9 @@
 using testing::Return;
 using testing::ReturnRef;
 
-namespace android {
+namespace android::scheduler {
 
-namespace scheduler {
+using MockLayer = android::mock::MockLayer;
 
 class LayerHistoryTest : public testing::Test {
 protected:
@@ -50,56 +51,63 @@
     static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
             LayerInfo::RefreshRateHistory::HISTORY_DURATION;
 
-    static constexpr Fps LO_FPS{30.f};
+    static constexpr Fps LO_FPS = 30_Hz;
     static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
 
-    static constexpr Fps HI_FPS{90.f};
+    static constexpr Fps HI_FPS = 90_Hz;
     static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
 
     LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
 
-    void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
-
-    LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
-    const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+    LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+    const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
 
     LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
-        return history().summarize(*mScheduler->refreshRateConfigs(), now);
+        // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
+        // however, for testing only, a stable order is required, therefore we sort the list here.
+        // Any tests requiring ordered results must create layers with names.
+        auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now);
+        std::sort(summary.begin(), summary.end(),
+                  [](const RefreshRateConfigs::LayerRequirement& a,
+                     const RefreshRateConfigs::LayerRequirement& b) -> bool {
+                      return a.name < b.name;
+                  });
+        return summary;
     }
 
     size_t layerCount() const { return mScheduler->layerHistorySize(); }
-    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
+        return history().mActiveLayerInfos.size();
+    }
 
     auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
-        const auto& infos = history().mLayerInfos;
-        return std::count_if(infos.begin(),
-                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
-                             [now](const auto& pair) { return pair.second->isFrequent(now); });
+        const auto& infos = history().mActiveLayerInfos;
+        return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+            return pair.second.second->isFrequent(now);
+        });
     }
 
     auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
-        const auto& infos = history().mLayerInfos;
-        return std::count_if(infos.begin(),
-                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
-                             [now](const auto& pair) { return pair.second->isAnimating(now); });
+        const auto& infos = history().mActiveLayerInfos;
+        return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+            return pair.second.second->isAnimating(now);
+        });
     }
 
     void setDefaultLayerVote(Layer* layer,
                              LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
-        for (auto& [layerUnsafe, info] : history().mLayerInfos) {
-            if (layerUnsafe == layer) {
-                info->setDefaultLayerVote(vote);
-                return;
-            }
+        auto [found, layerPair] = history().findLayer(layer->getSequence());
+        if (found != LayerHistory::layerStatus::NotFound) {
+            layerPair->second->setDefaultLayerVote(vote);
         }
     }
 
-    auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+    auto createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); }
     auto createLayer(std::string name) {
-        return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+        return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
     }
 
-    void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, Fps frameRate,
+    void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
                                Fps desiredRefreshRate, int numFrames) {
         LayerHistory::Summary summary;
         for (int i = 0; i < numFrames; i++) {
@@ -111,20 +119,21 @@
 
         ASSERT_EQ(1, summary.size());
         ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-        ASSERT_TRUE(desiredRefreshRate.equalsWithMargin(summary[0].desiredRefreshRate))
-                << "Frame rate is " << frameRate;
+        ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
     }
 
     std::shared_ptr<RefreshRateConfigs> mConfigs = std::make_shared<
             RefreshRateConfigs>(DisplayModes{DisplayMode::Builder(0)
                                                      .setId(DisplayModeId(0))
-                                                     .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                                     .setPhysicalDisplayId(
+                                                             PhysicalDisplayId::fromPort(0))
                                                      .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
                                                      .setGroup(0)
                                                      .build(),
                                              DisplayMode::Builder(1)
                                                      .setId(DisplayModeId(1))
-                                                     .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                                     .setPhysicalDisplayId(
+                                                             PhysicalDisplayId::fromPort(0))
                                                      .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
                                                      .setGroup(0)
                                                      .build()},
@@ -144,10 +153,12 @@
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
+    // history().registerLayer(layer, LayerHistory::LayerVoteType::Max);
+
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    const nsecs_t time = systemTime();
+    nsecs_t time = systemTime();
 
     // No layers returned if no layers are active.
     EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -159,6 +170,7 @@
         ASSERT_EQ(1, summarizeLayerHistory(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
+        time += LO_FPS_PERIOD;
     }
 
     // Max is returned since we have enough history but there is no timestamp votes.
@@ -167,6 +179,7 @@
         ASSERT_EQ(1, summarizeLayerHistory(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
+        time += LO_FPS_PERIOD;
     }
 }
 
@@ -211,7 +224,7 @@
 
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
-    EXPECT_TRUE(LO_FPS.equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+    EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 }
@@ -304,7 +317,7 @@
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameRateForLayerTree())
             .WillRepeatedly(
-                    Return(Layer::FrameRate(Fps(73.4f), Layer::FrameRateCompatibility::Default)));
+                    Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default)));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -317,7 +330,7 @@
 
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
@@ -326,7 +339,7 @@
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -336,7 +349,7 @@
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameRateForLayerTree())
             .WillRepeatedly(Return(
-                    Layer::FrameRate(Fps(73.4f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+                    Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -350,7 +363,7 @@
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
               summarizeLayerHistory(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
@@ -360,15 +373,15 @@
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
               summarizeLayerHistory(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
 
 TEST_F(LayerHistoryTest, multipleLayers) {
-    auto layer1 = createLayer();
-    auto layer2 = createLayer();
-    auto layer3 = createLayer();
+    auto layer1 = createLayer("A");
+    auto layer2 = createLayer("B");
+    auto layer3 = createLayer("C");
 
     EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
@@ -412,7 +425,7 @@
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
     ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_TRUE(HI_FPS.equalsWithMargin(summarizeLayerHistory(time)[1].desiredRefreshRate));
+    EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate);
 
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
@@ -427,7 +440,7 @@
 
     ASSERT_EQ(1, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
@@ -446,7 +459,7 @@
 
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
@@ -456,9 +469,9 @@
     summary = summarizeLayerHistory(time);
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[1].desiredRefreshRate));
+    EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
 
@@ -468,9 +481,9 @@
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[1].desiredRefreshRate));
+    EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
     EXPECT_EQ(2, layerCount());
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
@@ -485,7 +498,7 @@
 
     ASSERT_EQ(1, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
@@ -506,7 +519,7 @@
 
     ASSERT_EQ(1, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+    EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
@@ -583,12 +596,12 @@
     EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
             .WillRepeatedly(Return(
-                    Layer::FrameRate(Fps(60.0f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+                    Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
 
     EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
     EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
             .WillRepeatedly(Return(
-                    Layer::FrameRate(Fps(90.0f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+                    Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
 
     nsecs_t time = systemTime();
 
@@ -601,7 +614,7 @@
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
               summarizeLayerHistory(time)[0].vote);
-    EXPECT_TRUE(Fps(60.0f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
 }
@@ -652,6 +665,29 @@
     EXPECT_EQ(1, animatingLayerCount(time));
 }
 
+TEST_F(LayerHistoryTest, getFramerate) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
+}
+
 TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
@@ -659,7 +695,7 @@
 
     nsecs_t time = systemTime();
     for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
-        recordFramesAndExpect(layer, time, Fps(fps), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+        recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE);
     }
 }
 
@@ -669,13 +705,13 @@
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     nsecs_t time = systemTime();
-    recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
 
-    recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(30.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(30.0f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(60.0f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
 }
 
 TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
@@ -685,11 +721,11 @@
 
     nsecs_t time = systemTime();
 
-    recordFramesAndExpect(layer, time, Fps(27.10f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(26.90f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(26.00f), Fps(24.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(26.90f), Fps(24.0f), PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, Fps(27.10f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
 }
 
 class LayerHistoryTestParameterized : public LayerHistoryTest,
@@ -740,7 +776,7 @@
 
             bool max = false;
             bool min = false;
-            Fps heuristic{0.0};
+            Fps heuristic;
             for (const auto& layer : summarizeLayerHistory(time)) {
                 if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
                     heuristic = layer.desiredRefreshRate;
@@ -752,7 +788,7 @@
             }
 
             if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
-                EXPECT_TRUE(Fps(24.0f).equalsWithMargin(heuristic));
+                EXPECT_EQ(24_Hz, heuristic);
                 EXPECT_FALSE(max);
                 if (summarizeLayerHistory(time).size() == 2) {
                     EXPECT_TRUE(min);
@@ -766,8 +802,7 @@
                         ::testing::Values(1s, 2s, 3s, 4s, 5s));
 
 } // namespace
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index d6ce5e2..5c2d2e1 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -19,7 +19,9 @@
 
 #include <gtest/gtest.h>
 
-#include "Fps.h"
+#include <scheduler/Fps.h>
+
+#include "FpsOps.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/LayerInfo.h"
 
@@ -47,7 +49,7 @@
 
 TEST_F(LayerInfoTest, prefersPresentTime) {
     std::deque<FrameTimeData> frameTimes;
-    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kExpectedFps = 50_Hz;
     constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
     constexpr int kNumFrames = 10;
     for (int i = 1; i <= kNumFrames; i++) {
@@ -58,14 +60,12 @@
     setFrameTimes(frameTimes);
     const auto averageFrameTime = calculateAverageFrameTime();
     ASSERT_TRUE(averageFrameTime.has_value());
-    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
-    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
-            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+    ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
 }
 
 TEST_F(LayerInfoTest, fallbacksToQueueTimeIfNoPresentTime) {
     std::deque<FrameTimeData> frameTimes;
-    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kExpectedFps = 50_Hz;
     constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
     constexpr int kNumFrames = 10;
     for (int i = 1; i <= kNumFrames; i++) {
@@ -74,17 +74,15 @@
                                            .pendingModeChange = false});
     }
     setFrameTimes(frameTimes);
-    setLastRefreshRate(Fps(20.0f)); // Set to some valid value
+    setLastRefreshRate(20_Hz); // Set to some valid value.
     const auto averageFrameTime = calculateAverageFrameTime();
     ASSERT_TRUE(averageFrameTime.has_value());
-    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
-    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
-            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+    ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
 }
 
 TEST_F(LayerInfoTest, returnsNulloptIfThereWasConfigChange) {
     std::deque<FrameTimeData> frameTimesWithoutConfigChange;
-    const auto period = Fps(50.0f).getPeriodNsecs();
+    const auto period = (50_Hz).getPeriodNsecs();
     constexpr int kNumFrames = 10;
     for (int i = 1; i <= kNumFrames; i++) {
         frameTimesWithoutConfigChange.push_back(FrameTimeData{.presentTime = period * i,
@@ -124,9 +122,9 @@
 // Make sure that this doesn't influence the calculated average FPS.
 TEST_F(LayerInfoTest, ignoresSmallPeriods) {
     std::deque<FrameTimeData> frameTimes;
-    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kExpectedFps = 50_Hz;
     constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
-    constexpr auto kSmallPeriod = Fps(250.0f).getPeriodNsecs();
+    constexpr auto kSmallPeriod = (250_Hz).getPeriodNsecs();
     constexpr int kNumIterations = 10;
     for (int i = 1; i <= kNumIterations; i++) {
         frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
@@ -141,18 +139,16 @@
     setFrameTimes(frameTimes);
     const auto averageFrameTime = calculateAverageFrameTime();
     ASSERT_TRUE(averageFrameTime.has_value());
-    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
-    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
-            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+    ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
 }
 
 // There may be a big period of time between two frames. Make sure that
 // this doesn't influence the calculated average FPS.
 TEST_F(LayerInfoTest, ignoresLargePeriods) {
     std::deque<FrameTimeData> frameTimes;
-    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kExpectedFps = 50_Hz;
     constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
-    constexpr auto kLargePeriod = Fps(9.0f).getPeriodNsecs();
+    constexpr auto kLargePeriod = (9_Hz).getPeriodNsecs();
 
     auto record = [&](nsecs_t time) {
         frameTimes.push_back(
@@ -172,9 +168,7 @@
     setFrameTimes(frameTimes);
     const auto averageFrameTime = calculateAverageFrameTime();
     ASSERT_TRUE(averageFrameTime.has_value());
-    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
-    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
-            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+    ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index dbd51fe..bd4dc59 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -31,62 +31,51 @@
 
 using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
 
+struct NoOpCompositor final : ICompositor {
+    bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
+    void composite(nsecs_t) override {}
+    void sample() override {}
+} gNoOpCompositor;
+
 class TestableMessageQueue : public impl::MessageQueue {
-public:
-    class MockHandler : public MessageQueue::Handler {
-    public:
-        explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {}
-        ~MockHandler() override = default;
-        MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp));
+    struct MockHandler : MessageQueue::Handler {
+        using MessageQueue::Handler::Handler;
+
+        MOCK_METHOD(void, dispatchFrame, (int64_t, nsecs_t), (override));
     };
 
-    TestableMessageQueue() = default;
-    ~TestableMessageQueue() override = default;
+    explicit TestableMessageQueue(sp<MockHandler> handler)
+          : impl::MessageQueue(gNoOpCompositor, handler), mHandler(std::move(handler)) {}
 
-    void initHandler(const sp<MockHandler>& handler) { mHandler = handler; }
+public:
+    TestableMessageQueue() : TestableMessageQueue(sp<MockHandler>::make(*this)) {}
 
-    void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
-        vsyncCallback(vsyncTime, targetWakeupTime, readyTime);
-    }
+    using impl::MessageQueue::vsyncCallback;
+
+    const sp<MockHandler> mHandler;
 };
 
-class MockVSyncDispatch : public scheduler::VSyncDispatch {
-public:
-    MockVSyncDispatch() = default;
-    ~MockVSyncDispatch() override = default;
-
+struct MockVSyncDispatch : scheduler::VSyncDispatch {
     MOCK_METHOD2(registerCallback,
-                 CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+                 CallbackToken(const std::function<void(nsecs_t, nsecs_t, nsecs_t)>&, std::string));
     MOCK_METHOD1(unregisterCallback, void(CallbackToken));
     MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
     MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
-class MockTokenManager : public frametimeline::TokenManager {
-public:
-    MockTokenManager() = default;
-    ~MockTokenManager() override = default;
-
+struct MockTokenManager : frametimeline::TokenManager {
     MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
     MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
 };
 
-class MessageQueueTest : public testing::Test {
-public:
-    MessageQueueTest() = default;
-    ~MessageQueueTest() override = default;
-
+struct MessageQueueTest : testing::Test {
     void SetUp() override {
-        EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler));
-
         EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
         EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
         EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
     }
 
-    sp<TestableMessageQueue::MockHandler> mHandler =
-            new TestableMessageQueue::MockHandler(mEventQueue);
     MockVSyncDispatch mVSyncDispatch;
     MockTokenManager mTokenManager;
     TestableMessageQueue mEventQueue;
@@ -100,45 +89,49 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(MessageQueueTest, invalidate) {
+TEST_F(MessageQueueTest, commit) {
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
-    EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
 
     EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
-    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
-    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
 }
 
-TEST_F(MessageQueueTest, invalidateTwice) {
+TEST_F(MessageQueueTest, commitTwice) {
     InSequence s;
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
     EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
-    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
-    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
 
     EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
-    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
-    EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+    EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
 }
 
-TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
+TEST_F(MessageQueueTest, commitTwiceWithCallback) {
     InSequence s;
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
     EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
-    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
-    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
 
     const auto startTime = 100;
     const auto endTime = startTime + mDuration.count();
@@ -148,10 +141,10 @@
                 generateTokenForPredictions(
                         frametimeline::TimelineItem(startTime, endTime, presentTime)))
             .WillOnce(Return(vsyncId));
-    EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+    EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, presentTime)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.vsyncCallback(presentTime, startTime, endTime));
 
-    EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
 
     const auto timingAfterCallback =
             scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
@@ -159,10 +152,10 @@
                                                      .earliestVsync = presentTime};
 
     EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
-TEST_F(MessageQueueTest, invalidateWithDurationChange) {
+TEST_F(MessageQueueTest, commitWithDurationChange) {
     EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
 
     const auto timing =
@@ -171,7 +164,7 @@
                                                      .earliestVsync = 0};
 
     EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index 6916764..597e5e7 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -72,7 +72,8 @@
     mIdleTimer->stop();
 }
 
-TEST_F(OneShotTimerTest, resetTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
@@ -94,7 +95,8 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
-TEST_F(OneShotTimerTest, resetBackToBackTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetBackToBackTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
@@ -144,7 +146,8 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
-TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_idleTimerIdlesTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
@@ -169,7 +172,8 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
-TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_timeoutCallbackExecutionTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index c1dba2b..98746bc 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -21,21 +21,18 @@
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
+#include <ftl/enum.h>
 #include <gmock/gmock.h>
 #include <log/log.h>
-#include <thread>
-
 #include <ui/Size.h>
 
-#include "../../Scheduler/RefreshRateConfigs.h"
 #include "DisplayHardware/HWC2.h"
+#include "FpsOps.h"
 #include "Scheduler/RefreshRateConfigs.h"
 
 using namespace std::chrono_literals;
 
-namespace android {
-
-namespace scheduler {
+namespace android::scheduler {
 
 namespace hal = android::hardware::graphics::composer::hal;
 
@@ -98,27 +95,38 @@
     static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
     static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
     static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
+    static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7);
+    static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8);
+    static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9);
+    static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10);
 
     // Test configs
-    DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
-    DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
+    DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, (60_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig60Frac =
+            createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, (59.94_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs());
     DisplayModePtr mConfig90DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_90, 1, (90_Hz).getPeriodNsecs());
     DisplayModePtr mConfig90DifferentResolution =
-            createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs(), ui::Size(111, 222));
-    DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, Fps(72.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs(), ui::Size(111, 222));
+    DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, (72_Hz).getPeriodNsecs());
     DisplayModePtr mConfig72DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_72, 1, Fps(72.0f).getPeriodNsecs());
-    DisplayModePtr mConfig120 =
-            createDisplayMode(HWC_CONFIG_ID_120, 0, Fps(120.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_72, 1, (72_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig120 = createDisplayMode(HWC_CONFIG_ID_120, 0, (120_Hz).getPeriodNsecs());
     DisplayModePtr mConfig120DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_120, 1, Fps(120.0f).getPeriodNsecs());
-    DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_120, 1, (120_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, (30_Hz).getPeriodNsecs());
     DisplayModePtr mConfig30DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_30, 1, (30_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig30Frac =
+            createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, (29.97_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, (25_Hz).getPeriodNsecs());
     DisplayModePtr mConfig25DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
-    DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_25, 1, (25_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, (50_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, (24_Hz).getPeriodNsecs());
+    DisplayModePtr mConfig24Frac =
+            createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, (23.976_Hz).getPeriodNsecs());
 
     // Test device configurations
     // The positions of the configs in the arrays below MUST match their IDs. For example,
@@ -145,6 +153,11 @@
                                        mConfig50};
     DisplayModes m60_120Device = {mConfig60, mConfig120};
 
+    // This is a typical TV configuration.
+    DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25,
+                                                  mConfig30, mConfig30Frac, mConfig50,
+                                                  mConfig60, mConfig60Frac};
+
     // Expected RefreshRate objects
     RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)};
     RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
@@ -158,7 +171,6 @@
     RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)};
     RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)};
 
-private:
     DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
                                      ui::Size resolution = ui::Size());
 };
@@ -181,7 +193,7 @@
                                                          int64_t vsyncPeriod, ui::Size resolution) {
     return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
             .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId(0))
+            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
             .setVsyncPeriod(int32_t(vsyncPeriod))
             .setGroup(group)
             .setHeight(resolution.height)
@@ -190,9 +202,7 @@
 }
 
 namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
 TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
@@ -203,10 +213,8 @@
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {Fps(60), Fps(60)}}),
-              0);
-    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(20), Fps(40)}}),
-              0);
+    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
+    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {20_Hz, 40_Hz}}), 0);
 }
 
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
@@ -240,8 +248,7 @@
     ASSERT_EQ(mExpected60Config, minRate60);
     ASSERT_EQ(mExpected60Config, performanceRate60);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0);
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
@@ -266,8 +273,7 @@
     ASSERT_EQ(mExpected60Config, minRate60);
     ASSERT_EQ(mExpected60Config, performanceRate60);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0);
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
@@ -289,8 +295,7 @@
     ASSERT_EQ(mExpected60Config, minRate);
     ASSERT_EQ(mExpected90Config, performanceRate);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
 
     auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
     auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -313,8 +318,7 @@
         EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
     }
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
     {
         auto current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
@@ -328,14 +332,19 @@
 
     // If there are no layers we select the default frame rate, which is the max of the primary
     // range.
-    auto layers = std::vector<LayerRequirement>{};
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate({}, {}));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
-              0);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}),
+              NO_ERROR);
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate({}, {}));
+
+    // We select max even when this will cause a non-seamless switch.
+    refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                              /*currentConfigId=*/HWC_CONFIG_ID_60);
+    ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_90, /*allowGroupSwitching*/ true, {0_Hz, 90_Hz}}),
+              NO_ERROR);
+    EXPECT_EQ(mExpected90DifferentGroupConfig, refreshRateConfigs->getBestRefreshRate({}, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
@@ -343,142 +352,109 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
+    lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
+    lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
+    lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.name = "";
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_90, {Fps(90.0f), Fps(90.0f)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {Fps(0.0f), Fps(120.0f)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 120_Hz}}), 0);
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
@@ -487,44 +463,37 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
+    lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
+    lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
+    lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
@@ -532,37 +501,30 @@
             std::make_unique<RefreshRateConfigs>(m60_72_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
@@ -570,31 +532,27 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(48.0f);
+    lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(48.0f);
+    lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
@@ -602,91 +560,81 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr1.name = "24Hz Heuristic";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
@@ -695,91 +643,81 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr1.name = "24Hz Heuristic";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
@@ -787,37 +725,30 @@
             std::make_unique<RefreshRateConfigs>(m30_60Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
-    EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
@@ -825,60 +756,47 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
-    lr.desiredRefreshRate = Fps(45.0f);
+    lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
-    lr.desiredRefreshRate = Fps(30.0f);
+    lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
+    lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
-    lr.desiredRefreshRate = Fps(24.0f);
+    lr.desiredRefreshRate = 24_Hz;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
@@ -886,53 +804,45 @@
             std::make_unique<RefreshRateConfigs>(m30_60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(24.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = Fps(24.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = Fps(15.0f);
+    lr1.desiredRefreshRate = 15_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(45.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = Fps(30.0f);
+    lr1.desiredRefreshRate = 30_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = Fps(45.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
@@ -940,15 +850,15 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
-        lr.desiredRefreshRate = Fps(fps);
-        const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
-        EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+        lr.desiredRefreshRate = Fps::fromValue(fps);
+        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+        EXPECT_EQ(mExpected60Config, refreshRate)
+                << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
     }
 }
 
@@ -958,15 +868,15 @@
             std::make_unique<RefreshRateConfigs>(m60_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
-        lr.desiredRefreshRate = Fps(fps);
-        const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
-        EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+        lr.desiredRefreshRate = Fps::fromValue(fps);
+        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+        EXPECT_EQ(mExpected60Config, refreshRate)
+                << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
     }
 }
 
@@ -975,39 +885,35 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
     lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = Fps(90.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 90_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.desiredRefreshRate = Fps(90.0f);
+    lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = Fps(90.0f);
+    lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = Fps(60.0f);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, testInPolicy) {
-    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(Fps(60.000004f), Fps(60.000004f)));
-    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(Fps(59.0f), Fps(60.1f)));
-    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(75.0f), Fps(90.0f)));
-    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(60.0011f), Fps(90.0f)));
-    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(50.0f), Fps(59.998f)));
+    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(60.000004_Hz, 60.000004_Hz));
+    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(59_Hz, 60.1_Hz));
+    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(75_Hz, 90_Hz));
+    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(60.0011_Hz, 90_Hz));
+    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(50_Hz, 59.998_Hz));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
@@ -1015,15 +921,15 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
-        lr.desiredRefreshRate = Fps(fps);
-        const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
-        EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+        lr.desiredRefreshRate = Fps::fromValue(fps);
+        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+        EXPECT_EQ(mExpected90Config, refreshRate)
+                << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
     }
 }
 
@@ -1032,53 +938,47 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(30.0f);
+    lr1.desiredRefreshRate = 30_Hz;
     lr1.name = "30Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(30.0f);
+    lr1.desiredRefreshRate = 30_Hz;
     lr1.name = "30Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
@@ -1086,52 +986,46 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
@@ -1140,55 +1034,50 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false}, &consideredSignals);
+    refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals);
     EXPECT_EQ(false, consideredSignals.touch);
 
-    refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = false}, &consideredSignals);
+    refreshRateConfigs->getBestRefreshRate({}, {.touch = true}, &consideredSignals);
     EXPECT_EQ(true, consideredSignals.touch);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
-                                           &consideredSignals);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
     EXPECT_EQ(true, consideredSignals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
-                                           &consideredSignals);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
     EXPECT_EQ(false, consideredSignals.touch);
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
-                                           &consideredSignals);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
     EXPECT_EQ(true, consideredSignals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.desiredRefreshRate = Fps(60.0f);
+    lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
-                                           &consideredSignals);
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
     EXPECT_EQ(false, consideredSignals.touch);
 }
 
@@ -1197,40 +1086,128 @@
             std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     // Prepare a table with the vote and the expected refresh rate
-    const std::vector<std::pair<float, float>> testCases = {
-            {130, 120}, {120, 120}, {119, 120}, {110, 120},
+    const std::initializer_list<std::pair<Fps, Fps>> testCases = {
+            {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz},
 
-            {100, 90},  {90, 90},   {89, 90},
+            {100_Hz, 90_Hz},  {90_Hz, 90_Hz},   {89_Hz, 90_Hz},
 
-            {80, 72},   {73, 72},   {72, 72},   {71, 72},   {70, 72},
+            {80_Hz, 72_Hz},   {73_Hz, 72_Hz},   {72_Hz, 72_Hz},   {71_Hz, 72_Hz},   {70_Hz, 72_Hz},
 
-            {65, 60},   {60, 60},   {59, 60},   {58, 60},
+            {65_Hz, 60_Hz},   {60_Hz, 60_Hz},   {59_Hz, 60_Hz},   {58_Hz, 60_Hz},
 
-            {55, 90},   {50, 90},   {45, 90},
+            {55_Hz, 90_Hz},   {50_Hz, 90_Hz},   {45_Hz, 90_Hz},
 
-            {42, 120},  {40, 120},  {39, 120},
+            {42_Hz, 120_Hz},  {40_Hz, 120_Hz},  {39_Hz, 120_Hz},
 
-            {37, 72},   {36, 72},   {35, 72},
+            {37_Hz, 72_Hz},   {36_Hz, 72_Hz},   {35_Hz, 72_Hz},
 
-            {30, 60},
+            {30_Hz, 60_Hz},
     };
 
-    for (const auto& test : testCases) {
+    for (auto [desired, expected] : testCases) {
         lr.vote = LayerVoteType::ExplicitDefault;
-        lr.desiredRefreshRate = Fps(test.first);
+        lr.desiredRefreshRate = desired;
 
         std::stringstream ss;
-        ss << "ExplicitDefault " << test.first << " fps";
+        ss << "ExplicitDefault " << desired;
         lr.name = ss.str();
 
-        const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
-        EXPECT_TRUE(refreshRate.getFps().equalsWithMargin(Fps(test.second)))
-                << "Expecting " << test.first << "fps => " << test.second << "Hz";
+        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+        EXPECT_EQ(refreshRate.getFps(), expected);
+    }
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    // Test that 23.976 will choose 24 if 23.976 is not supported
+    {
+        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+        lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+        lr.desiredRefreshRate = 23.976_Hz;
+        lr.name = "ExplicitExactOrMultiple 23.976 Hz";
+        EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    }
+
+    // Test that 24 will choose 23.976 if 24 is not supported
+    {
+        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = 24_Hz;
+        lr.name = "ExplicitExactOrMultiple 24 Hz";
+        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    }
+
+    // Test that 29.97 will prefer 59.94 over 60 and 30
+    {
+        android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25,
+                                       mConfig30, mConfig60,     mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = 29.97_Hz;
+        lr.name = "ExplicitExactOrMultiple 29.97 Hz";
+        EXPECT_EQ(HWC_CONFIG_ID_60_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    // Test that voting for supported refresh rate will select this refresh rate
+    {
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+                                                     /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+        for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
+            lr.vote = LayerVoteType::ExplicitExact;
+            lr.desiredRefreshRate = desired;
+            std::stringstream ss;
+            ss << "ExplicitExact " << desired;
+            lr.name = ss.str();
+
+            auto selectedRefreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+            EXPECT_EQ(selectedRefreshRate.getFps(), lr.desiredRefreshRate);
+        }
+    }
+
+    // Test that 23.976 will choose 24 if 23.976 is not supported
+    {
+        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.vote = LayerVoteType::ExplicitExact;
+        lr.desiredRefreshRate = 23.976_Hz;
+        lr.name = "ExplicitExact 23.976 Hz";
+        EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    }
+
+    // Test that 24 will choose 23.976 if 24 is not supported
+    {
+        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = 24_Hz;
+        lr.name = "ExplicitExact 24 Hz";
+        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
     }
 }
 
@@ -1241,15 +1218,15 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_90);
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_90, {Fps(90.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
+                      {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
               0);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     RefreshRateConfigs::GlobalSignals consideredSignals;
     lr.vote = LayerVoteType::ExplicitDefault;
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitDefault";
     lr.focused = true;
     EXPECT_EQ(mExpected60Config,
@@ -1265,18 +1242,17 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {Fps(60.f), Fps(60.f)}, {Fps(60.f), Fps(90.f)}}),
+                      {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}),
               0);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::ExplicitDefault;
-    lr.desiredRefreshRate = Fps(90.0f);
+    lr.desiredRefreshRate = 90_Hz;
     lr.name = "90Hz ExplicitDefault";
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.idle = true}));
 }
 
 TEST_F(RefreshRateConfigsTest,
@@ -1286,72 +1262,61 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_90);
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_90, {Fps(90.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
+                      {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
               0);
 
     RefreshRateConfigs::GlobalSignals consideredSignals;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false},
-                                                     &consideredSignals));
+              refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals));
     EXPECT_EQ(false, consideredSignals.touch);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitExactOrMultiple";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::ExplicitDefault;
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitDefault";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Heuristic;
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Max;
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Max";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.vote = LayerVoteType::Min;
-    lr.desiredRefreshRate = Fps(60.0f);
+    lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Min";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
@@ -1361,17 +1326,15 @@
 
     // The default policy doesn't allow group switching. Verify that no
     // group switches are performed.
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = Fps(90.0f);
+    layer.desiredRefreshRate = 90_Hz;
     layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
 
-    ASSERT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
@@ -1383,16 +1346,14 @@
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = Fps(90.0f);
+    layer.desiredRefreshRate = 90_Hz;
     layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    ASSERT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
@@ -1405,16 +1366,14 @@
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
     // Verify that we won't change the group if seamless switch is required.
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = Fps(90.0f);
+    layer.desiredRefreshRate = 90_Hz;
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    ASSERT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
@@ -1429,16 +1388,14 @@
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = Fps(60.0f);
+    layer.desiredRefreshRate = 60_Hz;
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
-    ASSERT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
@@ -1455,17 +1412,15 @@
     // Verify that if the current config is in another group and there are no layers with
     // seamlessness=SeamedAndSeamless we'll go back to the default group.
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = Fps(60.0f);
+    layer.desiredRefreshRate = 60_Hz;
     layer.seamlessness = Seamlessness::Default;
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
 
-    ASSERT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
@@ -1481,9 +1436,9 @@
 
     // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
     // seamlessness=OnlySeamless can't change the mode group.
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].vote = LayerVoteType::ExplicitDefault;
-    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].desiredRefreshRate = 60_Hz;
     layers[0].seamlessness = Seamlessness::OnlySeamless;
     layers[0].name = "60Hz ExplicitDefault";
     layers[0].focused = true;
@@ -1491,13 +1446,11 @@
     layers.push_back(LayerRequirement{.weight = 0.5f});
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
-    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].desiredRefreshRate = 90_Hz;
     layers[1].name = "90Hz ExplicitDefault";
     layers[1].focused = false;
 
-    ASSERT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
@@ -1517,23 +1470,21 @@
     // For example, this may happen when a video playback requests and gets a seamed switch,
     // but another layer (with default seamlessness) starts animating. The animating layer
     // should not cause a seamed switch.
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].seamlessness = Seamlessness::Default;
-    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].desiredRefreshRate = 60_Hz;
     layers[0].focused = true;
     layers[0].vote = LayerVoteType::ExplicitDefault;
     layers[0].name = "60Hz ExplicitDefault";
 
     layers.push_back(LayerRequirement{.weight = 0.1f});
     layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
-    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].desiredRefreshRate = 90_Hz;
     layers[1].focused = true;
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    ASSERT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
@@ -1550,23 +1501,21 @@
     // Layer with seamlessness=Default can change the mode group if there's a not
     // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
     // when in split screen mode the user switches between the two visible applications.
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].seamlessness = Seamlessness::Default;
-    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].desiredRefreshRate = 60_Hz;
     layers[0].focused = true;
     layers[0].vote = LayerVoteType::ExplicitDefault;
     layers[0].name = "60Hz ExplicitDefault";
 
     layers.push_back(LayerRequirement{.weight = 0.7f});
     layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
-    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].desiredRefreshRate = 90_Hz;
     layers[1].focused = false;
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    ASSERT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
@@ -1580,22 +1529,18 @@
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    layer.desiredRefreshRate = Fps(60.0f);
+    layer.desiredRefreshRate = 60_Hz;
     layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "60Hz ExplicitExactOrMultiple";
     layer.focused = true;
 
-    ASSERT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
-    ASSERT_EQ(HWC_CONFIG_ID_120,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_120, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
@@ -1609,31 +1554,27 @@
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
-    auto layers = std::vector<
-            LayerRequirement>{LayerRequirement{.name = "60Hz ExplicitDefault",
-                                               .vote = LayerVoteType::ExplicitDefault,
-                                               .desiredRefreshRate = Fps(60.0f),
-                                               .seamlessness = Seamlessness::SeamedAndSeamless,
-                                               .weight = 0.5f,
-                                               .focused = false},
-                              LayerRequirement{.name = "25Hz ExplicitExactOrMultiple",
-                                               .vote = LayerVoteType::ExplicitExactOrMultiple,
-                                               .desiredRefreshRate = Fps(25.0f),
-                                               .seamlessness = Seamlessness::OnlySeamless,
-                                               .weight = 1.0f,
-                                               .focused = true}};
+    std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
+                                             .vote = LayerVoteType::ExplicitDefault,
+                                             .desiredRefreshRate = 60_Hz,
+                                             .seamlessness = Seamlessness::SeamedAndSeamless,
+                                             .weight = 0.5f,
+                                             .focused = false},
+                                            {.name = "25Hz ExplicitExactOrMultiple",
+                                             .vote = LayerVoteType::ExplicitExactOrMultiple,
+                                             .desiredRefreshRate = 25_Hz,
+                                             .seamlessness = Seamlessness::OnlySeamless,
+                                             .weight = 1.f,
+                                             .focused = true}};
 
-    ASSERT_EQ(HWC_CONFIG_ID_50,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_50, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 
     auto& seamedLayer = layers[0];
-    seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = Fps(30.0f);
+    seamedLayer.desiredRefreshRate = 30_Hz;
+    seamedLayer.name = "30Hz ExplicitDefault";
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30);
 
-    ASSERT_EQ(HWC_CONFIG_ID_25,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_25, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
@@ -1647,14 +1588,10 @@
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.name = "Min",
-                                                                 .vote = LayerVoteType::Min,
-                                                                 .weight = 1.f,
-                                                                 .focused = true}};
+    std::vector<LayerRequirement> layers = {
+            {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
 
-    ASSERT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getModeId());
+    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
@@ -1662,59 +1599,58 @@
             std::make_unique<RefreshRateConfigs>(m30_60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
 
+    struct Args {
+        bool touch = false;
+        bool focused = true;
+    };
+
     // Return the config ID from calling getBestRefreshRate() for a single layer with the
     // given voteType and fps.
-    auto getFrameRate = [&](LayerVoteType voteType, Fps fps, bool touchActive = false,
-                            bool focused = true) -> DisplayModeId {
+    auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId {
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = fps;
-        layers[0].focused = focused;
-        return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
-                .getModeId();
+        layers[0].focused = args.focused;
+        return refreshRateConfigs->getBestRefreshRate(layers, {.touch = args.touch}).getModeId();
     };
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {Fps(30.f), Fps(60.f)}, {Fps(30.f), Fps(90.f)}}),
+                      {HWC_CONFIG_ID_60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}),
               0);
-    EXPECT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
-                      .getModeId());
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f)));
+    EXPECT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate({}, {}).getModeId());
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
 
-    // Layers not focused are not allowed to override primary config
+    // Unfocused layers are not allowed to override primary config.
     EXPECT_EQ(HWC_CONFIG_ID_60,
-              getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f), /*touch=*/false,
-                           /*focused=*/false));
+              getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
     EXPECT_EQ(HWC_CONFIG_ID_60,
-              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f), /*touch=*/false,
-                           /*focused=*/false));
+              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
 
     // Touch boost should be restricted to the primary range.
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f), /*touch=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
     // When we're higher than the primary range max due to a layer frame rate setting, touch boost
     // shouldn't drag us back down to the primary range max.
     EXPECT_EQ(HWC_CONFIG_ID_90,
-              getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f), /*touch=*/true));
+              getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
     EXPECT_EQ(HWC_CONFIG_ID_60,
-              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f), /*touch=*/true));
+              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {Fps(60.f), Fps(60.f)}, {Fps(60.f), Fps(60.f)}}),
+                      {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}),
               0);
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f)));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f)));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
 }
 
 TEST_F(RefreshRateConfigsTest, idle) {
@@ -1722,12 +1658,12 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
 
     const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
         layers[0].vote = voteType;
-        layers[0].desiredRefreshRate = Fps(90.f);
+        layers[0].desiredRefreshRate = 90_Hz;
         RefreshRateConfigs::GlobalSignals consideredSignals;
         const auto configId =
                 refreshRateConfigs
@@ -1740,7 +1676,7 @@
     };
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {Fps(60.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
+                      {HWC_CONFIG_ID_60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
               0);
 
     // Idle should be lower priority than touch boost.
@@ -1771,8 +1707,7 @@
 
     // Idle should be applied rather than the current config when there are no layers.
     EXPECT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
-                      .getModeId());
+              refreshRateConfigs->getBestRefreshRate({}, {.idle = true}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
@@ -1781,23 +1716,23 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
-        const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, Fps(fps));
+        const auto knownFrameRate =
+                findClosestKnownFrameRate(*refreshRateConfigs, Fps::fromValue(fps));
         Fps expectedFrameRate;
         if (fps < 26.91f) {
-            expectedFrameRate = Fps(24.0f);
+            expectedFrameRate = 24_Hz;
         } else if (fps < 37.51f) {
-            expectedFrameRate = Fps(30.0f);
+            expectedFrameRate = 30_Hz;
         } else if (fps < 52.51f) {
-            expectedFrameRate = Fps(45.0f);
+            expectedFrameRate = 45_Hz;
         } else if (fps < 66.01f) {
-            expectedFrameRate = Fps(60.0f);
+            expectedFrameRate = 60_Hz;
         } else if (fps < 81.01f) {
-            expectedFrameRate = Fps(72.0f);
+            expectedFrameRate = 72_Hz;
         } else {
-            expectedFrameRate = Fps(90.0f);
+            expectedFrameRate = 90_Hz;
         }
-        EXPECT_TRUE(expectedFrameRate.equalsWithMargin(knownFrameRate))
-                << "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate;
+        EXPECT_EQ(expectedFrameRate, knownFrameRate);
     }
 }
 
@@ -1806,38 +1741,32 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    struct ExpectedRate {
-        Fps rate;
-        const RefreshRate& expected;
+    struct Expectation {
+        Fps fps;
+        const RefreshRate& refreshRate;
     };
 
-    /* clang-format off */
-    std::vector<ExpectedRate> knownFrameRatesExpectations = {
-        {Fps(24.0f), mExpected60Config},
-        {Fps(30.0f), mExpected60Config},
-        {Fps(45.0f), mExpected90Config},
-        {Fps(60.0f), mExpected60Config},
-        {Fps(72.0f), mExpected90Config},
-        {Fps(90.0f), mExpected90Config},
+    const std::initializer_list<Expectation> knownFrameRatesExpectations = {
+            {24_Hz, mExpected60Config}, {30_Hz, mExpected60Config}, {45_Hz, mExpected90Config},
+            {60_Hz, mExpected60Config}, {72_Hz, mExpected90Config}, {90_Hz, mExpected90Config},
     };
-    /* clang-format on */
 
     // Make sure the test tests all the known frame rate
     const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
-    const auto equal =
-            std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
-                       knownFrameRatesExpectations.begin(),
-                       [](Fps a, const ExpectedRate& b) { return a.equalsWithMargin(b.rate); });
+    const bool equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+                                  knownFrameRatesExpectations.begin(),
+                                  [](Fps fps, const Expectation& expected) {
+                                      return isApproxEqual(fps, expected.fps);
+                                  });
     EXPECT_TRUE(equal);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::Heuristic;
-    for (const auto& expectedRate : knownFrameRatesExpectations) {
-        layer.desiredRefreshRate = expectedRate.rate;
-        const auto& refreshRate =
-                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
-        EXPECT_EQ(expectedRate.expected, refreshRate);
+
+    for (const auto& [fps, refreshRate] : knownFrameRatesExpectations) {
+        layer.desiredRefreshRate = fps;
+        EXPECT_EQ(refreshRate, refreshRateConfigs->getBestRefreshRate(layers, {}));
     }
 }
 
@@ -1846,40 +1775,33 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 0.5f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
     auto& explicitExactOrMultipleLayer = layers[1];
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
     explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
 
     explicitExactLayer.vote = LayerVoteType::ExplicitExact;
     explicitExactLayer.name = "ExplicitExact";
-    explicitExactLayer.desiredRefreshRate = Fps(30);
+    explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
-    explicitExactOrMultipleLayer.desiredRefreshRate = Fps(120);
-    explicitExactLayer.desiredRefreshRate = Fps(60);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
+    explicitExactLayer.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    explicitExactLayer.desiredRefreshRate = Fps(72);
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactLayer.desiredRefreshRate = 72_Hz;
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    explicitExactLayer.desiredRefreshRate = Fps(90);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactLayer.desiredRefreshRate = 90_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    explicitExactLayer.desiredRefreshRate = Fps(120);
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactLayer.desiredRefreshRate = 120_Hz;
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
@@ -1888,40 +1810,33 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 0.5f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
     auto& explicitExactOrMultipleLayer = layers[1];
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
     explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
 
     explicitExactLayer.vote = LayerVoteType::ExplicitExact;
     explicitExactLayer.name = "ExplicitExact";
-    explicitExactLayer.desiredRefreshRate = Fps(30);
+    explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
-    explicitExactOrMultipleLayer.desiredRefreshRate = Fps(120);
-    explicitExactLayer.desiredRefreshRate = Fps(60);
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
+    explicitExactLayer.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    explicitExactLayer.desiredRefreshRate = Fps(72);
-    EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactLayer.desiredRefreshRate = 72_Hz;
+    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    explicitExactLayer.desiredRefreshRate = Fps(90);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactLayer.desiredRefreshRate = 90_Hz;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 
-    explicitExactLayer.desiredRefreshRate = Fps(120);
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    explicitExactLayer.desiredRefreshRate = 120_Hz;
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
@@ -1932,26 +1847,20 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     setLastBestRefreshRateInvocation(*refreshRateConfigs,
-                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
-                                                                          LayerRequirement>(),
-                                                                  .globalSignals = {.touch = true,
+                                     GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
                                                                                     .idle = true},
                                                                   .outSignalsConsidered =
-                                                                          {.touch = true,
-                                                                           .idle = false},
+                                                                          {.touch = true},
                                                                   .resultingBestRefreshRate =
                                                                           createRefreshRate(
                                                                                   mConfig90)});
 
     EXPECT_EQ(createRefreshRate(mConfig90),
-              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
-                                                     {.touch = true, .idle = true}));
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true}));
 
-    const GlobalSignals cachedSignalsConsidered{.touch = true, .idle = false};
+    const GlobalSignals cachedSignalsConsidered{.touch = true};
     setLastBestRefreshRateInvocation(*refreshRateConfigs,
-                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
-                                                                          LayerRequirement>(),
-                                                                  .globalSignals = {.touch = true,
+                                     GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
                                                                                     .idle = true},
                                                                   .outSignalsConsidered =
                                                                           cachedSignalsConsidered,
@@ -1961,8 +1870,7 @@
 
     GlobalSignals signalsConsidered;
     EXPECT_EQ(createRefreshRate(mConfig30),
-              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
-                                                     {.touch = true, .idle = true},
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true},
                                                      &signalsConsidered));
 
     EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
@@ -1977,8 +1885,7 @@
     ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());
 
     GlobalSignals globalSignals{.touch = true, .idle = true};
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 0.5f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     const auto lastResult =
             refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
                                                    /* outSignalsConsidered */ nullptr);
@@ -2002,30 +1909,81 @@
             std::make_unique<RefreshRateConfigs>(m60_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 0.5f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
     auto& explicitExactOrMultipleLayer = layers[1];
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
     explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
 
     explicitExactLayer.vote = LayerVoteType::ExplicitExact;
     explicitExactLayer.name = "ExplicitExact";
-    explicitExactLayer.desiredRefreshRate = Fps(30);
+    explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
 
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
+    auto& explicitDefaultLayer = layers[0];
+    auto& explicitExactOrMultipleLayer = layers[1];
+
+    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+    explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
+    explicitDefaultLayer.name = "ExplicitDefault";
+    explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
+
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) {
+    constexpr int kMinRefreshRate = 10;
+    constexpr int kMaxRefreshRate = 240;
+
+    DisplayModes displayModes;
+    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+        constexpr int32_t kGroup = 0;
+        const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
+        displayModes.push_back(
+                createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs()));
+    }
+
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(displayModes,
+                                                 /*currentConfigId=*/displayModes[0]->getId());
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+        layers[0].desiredRefreshRate = fps;
+        layers[0].vote = vote;
+        EXPECT_EQ(fps.getIntValue(),
+                  refreshRateConfigs->getBestRefreshRate(layers, {}).getFps().getIntValue())
+                << "Failed for " << ftl::enum_string(vote);
+    };
+
+    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+        const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
+        testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+    }
 }
 
 TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
@@ -2044,18 +2002,15 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
 
     // SetPolicy(60, 90), current 60Hz => TurnOn.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(90)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 90_Hz}}), 0);
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
 
     // SetPolicy(60, 60), current 60Hz => TurnOff
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 
     // SetPolicy(90, 90), current 90Hz => TurnOff.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 }
 
@@ -2067,23 +2022,19 @@
             std::make_unique<RefreshRateConfigs>(m60_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_120);
     // SetPolicy(0, 60), current 60Hz => TurnOn.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(0), Fps(60)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 60_Hz}}), 0);
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
 
     // SetPolicy(60, 60), current 60Hz => TurnOff.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 
     // SetPolicy(60, 120), current 60Hz => TurnOn.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(120)}}),
-              0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 120_Hz}}), 0);
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
 
     // SetPolicy(120, 120), current 120Hz => TurnOff.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_120, {Fps(120), Fps(120)}}),
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_120, {120_Hz, 120_Hz}}),
               0);
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 }
@@ -2093,7 +2044,7 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_30);
 
-    const auto frameRate = Fps(30.f);
+    const auto frameRate = 30_Hz;
     Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
     EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
@@ -2115,12 +2066,38 @@
 
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
+    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, 22.5_Hz));
 
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(25.f)));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(23.976f)));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(30.f), Fps(29.97f)));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 25_Hz));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 23.976_Hz));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(30_Hz, 29.97_Hz));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(60_Hz, 59.94_Hz));
+}
+
+TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
+
+    const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz};
+    for (auto refreshRate : refreshRates) {
+        EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(refreshRate, refreshRate));
+    }
+
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 25_Hz));
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
@@ -2128,9 +2105,7 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_120);
 
-    auto layers = std::vector<LayerRequirement>{};
-    ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false)
-                        .empty());
+    ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides({}, 120_Hz, {}).empty());
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
@@ -2139,42 +2114,36 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_120, config);
 
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
     layers[0].ownerUid = 1234;
-    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].desiredRefreshRate = 60_Hz;
     layers[0].vote = LayerVoteType::ExplicitDefault;
-    auto frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::NoVote;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Min;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Max;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Heuristic;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_TRUE(frameRateOverrides.empty());
 }
 
@@ -2184,37 +2153,32 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_120, config);
 
-    auto layers = std::vector<LayerRequirement>{
-            LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
-            LayerRequirement{.ownerUid = 5678, .weight = 1.0f},
-    };
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+                                            {.ownerUid = 5678, .weight = 1.f}};
 
     layers[0].name = "Test layer 1234";
-    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].desiredRefreshRate = 60_Hz;
     layers[0].vote = LayerVoteType::ExplicitDefault;
 
     layers[1].name = "Test layer 5678";
-    layers[1].desiredRefreshRate = Fps(30.0f);
+    layers[1].desiredRefreshRate = 30_Hz;
     layers[1].vote = LayerVoteType::ExplicitDefault;
-    auto frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
 
     ASSERT_EQ(2, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
     ASSERT_EQ(1, frameRateOverrides.count(5678));
-    ASSERT_EQ(30.0f, frameRateOverrides.at(5678).getValue());
+    ASSERT_EQ(30_Hz, frameRateOverrides.at(5678));
 
     layers[1].vote = LayerVoteType::Heuristic;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[1].ownerUid = 1234;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_TRUE(frameRateOverrides.empty());
 }
 
@@ -2224,54 +2188,44 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
                                                  HWC_CONFIG_ID_120, config);
 
-    auto layers = std::vector<LayerRequirement>{
-            LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
-    };
-
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
     layers[0].name = "Test layer";
-    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].desiredRefreshRate = 60_Hz;
     layers[0].vote = LayerVoteType::ExplicitDefault;
 
-    auto frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExact;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
     ASSERT_EQ(1, frameRateOverrides.size());
     ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
     ASSERT_TRUE(frameRateOverrides.empty());
 }
 
 } // namespace
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 35033ea..1e6e336 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -21,7 +21,6 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
 #include "EffectLayer.h"
 #include "Layer.h"
@@ -60,7 +59,6 @@
     static constexpr int32_t PRIORITY_UNSET = -1;
 
     void setupScheduler();
-    sp<BufferQueueLayer> createBufferQueueLayer();
     sp<BufferStateLayer> createBufferStateLayer();
     sp<EffectLayer> createEffectLayer();
 
@@ -90,24 +88,17 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-sp<BufferQueueLayer> RefreshRateSelectionTest::createBufferQueueLayer() {
-    sp<Client> client;
-    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
-                           LAYER_FLAGS, LayerMetadata());
-    return new BufferQueueLayer(args);
-}
 
 sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
     sp<Client> client;
-    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
-                           LAYER_FLAGS, LayerMetadata());
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
+                           LayerMetadata());
     return new BufferStateLayer(args);
 }
 
 sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
     sp<Client> client;
-    LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
-                           LayerMetadata());
+    LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
     return new EffectLayer(args);
 }
 
@@ -149,46 +140,6 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateSelectionTest, testPriorityOnBufferQueueLayers) {
-    mParent = createBufferQueueLayer();
-    mChild = createBufferQueueLayer();
-    setParent(mChild.get(), mParent.get());
-    mGrandChild = createBufferQueueLayer();
-    setParent(mGrandChild.get(), mChild.get());
-
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
-    // Child has its own priority.
-    mGrandChild->setFrameRateSelectionPriority(1);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
-    // Child inherits from his parent.
-    mChild->setFrameRateSelectionPriority(1);
-    commitTransaction(mChild.get());
-    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mGrandChild.get());
-
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
-    // Grandchild inherits from his grand parent.
-    mParent->setFrameRateSelectionPriority(1);
-    commitTransaction(mParent.get());
-    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mChild.get());
-    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
 TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
     mParent = createBufferStateLayer();
     mChild = createBufferStateLayer();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 12b155b..df4a9c4 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -35,8 +31,7 @@
 using testing::_;
 using testing::AtLeast;
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 class RefreshRateStatsTest : public testing::Test {
 protected:
@@ -81,61 +76,60 @@
                                                        int64_t vsyncPeriod) {
     return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value()))
             .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId(0))
+            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
             .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
             .setGroup(group)
             .build();
 }
 
 namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
 TEST_F(RefreshRateStatsTest, oneConfigTest) {
     init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
-    ASSERT_EQ(1, times.size());
-    EXPECT_NE(0u, times.count("ScreenOff"));
-    // Setting up tests on mobile harness can be flaky with time passing, so testing for
-    // exact time changes can result in flaxy numbers. To avoid that remember old
-    // numbers to make sure the correct values are increasing in the next test.
-    auto screenOff = times["ScreenOff"];
+    auto times = mRefreshRateStats->getTotalTimes();
+    ASSERT_TRUE(times.contains("ScreenOff"));
+    EXPECT_EQ(1u, times.size());
 
     // Screen is off by default.
+    std::chrono::milliseconds screenOff = times.get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(0u, times.count("90.00fps"));
+
+    EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+    EXPECT_FALSE(times.contains("90.00 Hz"));
 
     const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
     mRefreshRateStats->setRefreshRate(config0Fps);
     mRefreshRateStats->setPowerMode(PowerMode::ON);
-    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+    screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_EQ(screenOff, times["ScreenOff"]);
-    ASSERT_EQ(1u, times.count("90.00fps"));
-    EXPECT_LT(0, times["90.00fps"]);
+
+    EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+    ASSERT_TRUE(times.contains("90.00 Hz"));
+    EXPECT_LT(0ms, times.get("90.00 Hz")->get());
 
     mRefreshRateStats->setPowerMode(PowerMode::DOZE);
-    auto ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
+    const auto ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(ninety, times["90.00fps"]);
+
+    EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+    EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
 
     mRefreshRateStats->setRefreshRate(config0Fps);
-    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+    screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
+
     // Because the power mode is not PowerMode::ON, switching the config
     // does not update refresh rates that come from the config.
-    EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(ninety, times["90.00fps"]);
+    EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+    EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
 }
 
 TEST_F(RefreshRateStatsTest, twoConfigsTest) {
@@ -146,78 +140,81 @@
     EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
-    ASSERT_EQ(1, times.size());
-    EXPECT_NE(0u, times.count("ScreenOff"));
-    // Setting up tests on mobile harness can be flaky with time passing, so testing for
-    // exact time changes can result in flaxy numbers. To avoid that remember old
-    // numbers to make sure the correct values are increasing in the next test.
-    auto screenOff = times["ScreenOff"];
+    auto times = mRefreshRateStats->getTotalTimes();
+    ASSERT_TRUE(times.contains("ScreenOff"));
+    EXPECT_EQ(1u, times.size());
 
     // Screen is off by default.
+    std::chrono::milliseconds screenOff = times.get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_LT(screenOff, times["ScreenOff"]);
+
+    EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+    EXPECT_FALSE(times.contains("60.00 Hz"));
+    EXPECT_FALSE(times.contains("90.00 Hz"));
 
     const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
     const auto config1Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_1).getFps();
     mRefreshRateStats->setRefreshRate(config0Fps);
     mRefreshRateStats->setPowerMode(PowerMode::ON);
-    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+    screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_EQ(screenOff, times["ScreenOff"]);
-    ASSERT_EQ(1u, times.count("90.00fps"));
-    EXPECT_LT(0, times["90.00fps"]);
+
+    EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+    ASSERT_TRUE(times.contains("90.00 Hz"));
+    EXPECT_LT(0ms, times.get("90.00 Hz")->get());
 
     // When power mode is normal, time for configs updates.
     mRefreshRateStats->setRefreshRate(config1Fps);
-    auto ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
+    auto ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(ninety, times["90.00fps"]);
-    ASSERT_EQ(1u, times.count("60.00fps"));
-    EXPECT_LT(0, times["60.00fps"]);
+
+    EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+    EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+    ASSERT_TRUE(times.contains("60.00 Hz"));
+    EXPECT_LT(0ms, times.get("60.00 Hz")->get());
 
     mRefreshRateStats->setRefreshRate(config0Fps);
-    auto sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
+    auto sixty = mRefreshRateStats->getTotalTimes().get("60.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_LT(ninety, times["90.00fps"]);
-    EXPECT_EQ(sixty, times["60.00fps"]);
+
+    EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+    EXPECT_LT(ninety, times.get("90.00 Hz")->get());
+    EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
 
     mRefreshRateStats->setRefreshRate(config1Fps);
-    ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
+    ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(ninety, times["90.00fps"]);
-    EXPECT_LT(sixty, times["60.00fps"]);
+
+    EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+    EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+    EXPECT_LT(sixty, times.get("60.00 Hz")->get());
 
     // Because the power mode is not PowerMode::ON, switching the config
     // does not update refresh rates that come from the config.
     mRefreshRateStats->setPowerMode(PowerMode::DOZE);
     mRefreshRateStats->setRefreshRate(config0Fps);
-    sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
+    sixty = mRefreshRateStats->getTotalTimes().get("60.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(ninety, times["90.00fps"]);
-    EXPECT_EQ(sixty, times["60.00fps"]);
+
+    EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+    EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+    EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
 
     mRefreshRateStats->setRefreshRate(config1Fps);
-    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+    screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(ninety, times["90.00fps"]);
-    EXPECT_EQ(sixty, times["60.00fps"]);
-}
-} // namespace
-} // namespace scheduler
-} // namespace android
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
+    EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+    EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+    EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 5713c2f..f48abb7 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -28,13 +28,17 @@
 #include "mock/MockLayer.h"
 #include "mock/MockSchedulerCallback.h"
 
+namespace android::scheduler {
+
 using testing::_;
 using testing::Return;
 
-namespace android {
 namespace {
 
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999);
+using MockEventThread = android::mock::EventThread;
+using MockLayer = android::mock::MockLayer;
+
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
 
 class SchedulerTest : public testing::Test {
 protected:
@@ -44,49 +48,41 @@
               : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {}
         ~MockEventThreadConnection() = default;
 
-        MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
-        MOCK_METHOD1(setVsyncRate, status_t(uint32_t count));
-        MOCK_METHOD0(requestNextVsync, void());
+        MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel));
+        MOCK_METHOD1(setVsyncRate, binder::Status(int count));
+        MOCK_METHOD0(requestNextVsync, binder::Status());
     };
 
     SchedulerTest();
 
     const DisplayModePtr mode60 = DisplayMode::Builder(0)
                                           .setId(DisplayModeId(0))
-                                          .setPhysicalDisplayId(PhysicalDisplayId(0))
-                                          .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
+                                          .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+                                          .setVsyncPeriod((60_Hz).getPeriodNsecs())
                                           .setGroup(0)
                                           .build();
     const DisplayModePtr mode120 = DisplayMode::Builder(1)
                                            .setId(DisplayModeId(1))
-                                           .setPhysicalDisplayId(PhysicalDisplayId(0))
-                                           .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
+                                           .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+                                           .setVsyncPeriod((120_Hz).getPeriodNsecs())
                                            .setGroup(0)
                                            .build();
 
-    std::shared_ptr<scheduler::RefreshRateConfigs> mConfigs =
-            std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
+    std::shared_ptr<RefreshRateConfigs> mConfigs =
+            std::make_shared<RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
-
-    // The scheduler should initially disable VSYNC.
-    struct ExpectDisableVsync {
-        ExpectDisableVsync(mock::SchedulerCallback& callback) {
-            EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1);
-        }
-    } mExpectDisableVsync{mSchedulerCallback};
-
     TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
 
-    Scheduler::ConnectionHandle mConnectionHandle;
-    mock::EventThread* mEventThread;
+    ConnectionHandle mConnectionHandle;
+    MockEventThread* mEventThread;
     sp<MockEventThreadConnection> mEventThreadConnection;
 
     TestableSurfaceFlinger mFlinger;
 };
 
 SchedulerTest::SchedulerTest() {
-    auto eventThread = std::make_unique<mock::EventThread>();
+    auto eventThread = std::make_unique<MockEventThread>();
     mEventThread = eventThread.get();
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
 
@@ -106,7 +102,7 @@
 } // namespace
 
 TEST_F(SchedulerTest, invalidConnectionHandle) {
-    Scheduler::ConnectionHandle handle;
+    ConnectionHandle handle;
 
     const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
 
@@ -163,12 +159,12 @@
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
     // The layer is registered at creation time and deregistered at destruction time.
-    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+    sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
 
     // recordLayerHistory should be a noop
-    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+    ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
-    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+    ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
 
     constexpr bool kPowerStateNormal = true;
     mScheduler->setDisplayPowerState(kPowerStateNormal);
@@ -181,25 +177,23 @@
 }
 
 TEST_F(SchedulerTest, updateDisplayModes) {
-    ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize());
-    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
-    ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
+    ASSERT_EQ(0u, mScheduler->layerHistorySize());
+    sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+    ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
     mScheduler->setRefreshRateConfigs(
-            std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
-                                                            mode60->getId()));
+            std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
 
-    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+    ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
-    ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers());
+    ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
 }
 
-TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
-    // If the optional fields are cleared, the function should return before
-    // onModeChange is called.
-    mScheduler->clearOptionalFieldsInFeatures();
-    EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
+TEST_F(SchedulerTest, dispatchCachedReportedMode) {
+    mScheduler->clearCachedReportedMode();
+
     EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
+    EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
 }
 
 TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
@@ -211,31 +205,30 @@
 
     // If the handle is incorrect, the function should return before
     // onModeChange is called.
-    Scheduler::ConnectionHandle invalidHandle = {.id = 123};
+    ConnectionHandle invalidHandle = {.id = 123};
     EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
     EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
 }
 
 TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
-    EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(Fps(60), 30ms));
-    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(Fps(90), 30ms));
-    EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(Fps(120), 30ms));
+    EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms));
+    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms));
+    EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(120_Hz, 30ms));
 
-    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(Fps(60), 40ms));
+    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
 
-    EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(Fps(60), 10ms));
+    EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
 }
 
 MATCHER(Is120Hz, "") {
-    return arg.getFps().equalsWithMargin(Fps(120.f));
+    return isApproxEqual(arg.getFps(), 120_Hz);
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
     mScheduler->setRefreshRateConfigs(
-            std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
-                                                            mode60->getId()));
+            std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
 
-    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+    sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
 
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
 
@@ -249,4 +242,4 @@
     mScheduler->chooseRefreshRateForContent();
 }
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index a4e9d20..fe5f9e0 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -24,16 +24,15 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
 #include "EffectLayer.h"
 #include "Layer.h"
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
+#include "FpsOps.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
 #include "mock/MockVsyncController.h"
 
 namespace android {
@@ -49,6 +48,8 @@
 
 using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
 
+using scheduler::LayerHistory;
+
 using FrameRate = Layer::FrameRate;
 using FrameRateCompatibility = Layer::FrameRateCompatibility;
 
@@ -65,24 +66,13 @@
     static constexpr uint32_t LAYER_FLAGS = 0;
 };
 
-class BufferQueueLayerFactory : public LayerFactory {
-public:
-    std::string name() override { return "BufferQueueLayer"; }
-    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
-        sp<Client> client;
-        LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
-                               LAYER_FLAGS, LayerMetadata());
-        return new BufferQueueLayer(args);
-    }
-};
-
 class BufferStateLayerFactory : public LayerFactory {
 public:
     std::string name() override { return "BufferStateLayer"; }
     sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
         sp<Client> client;
-        LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
-                               LAYER_FLAGS, LayerMetadata());
+        LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
+                               LayerMetadata());
         return new BufferStateLayer(args);
     }
 };
@@ -92,7 +82,7 @@
     std::string name() override { return "EffectLayer"; }
     sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
         sp<Client> client;
-        LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+        LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS,
                                LayerMetadata());
         return new EffectLayer(args);
     }
@@ -108,12 +98,11 @@
  */
 class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
 protected:
-    const FrameRate FRAME_RATE_VOTE1 = FrameRate(Fps(67.f), FrameRateCompatibility::Default);
-    const FrameRate FRAME_RATE_VOTE2 =
-            FrameRate(Fps(14.f), FrameRateCompatibility::ExactOrMultiple);
-    const FrameRate FRAME_RATE_VOTE3 = FrameRate(Fps(99.f), FrameRateCompatibility::NoVote);
-    const FrameRate FRAME_RATE_TREE = FrameRate(Fps(0.f), FrameRateCompatibility::NoVote);
-    const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(0.f), FrameRateCompatibility::Default);
+    const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default);
+    const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple);
+    const FrameRate FRAME_RATE_VOTE3 = FrameRate(99_Hz, FrameRateCompatibility::NoVote);
+    const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
+    const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(), FrameRateCompatibility::Default);
 
     SetFrameRateTest();
 
@@ -124,7 +113,6 @@
     void commitTransaction();
 
     TestableSurfaceFlinger mFlinger;
-    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
 
     std::vector<sp<Layer>> mLayers;
 };
@@ -134,12 +122,9 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    mFlinger.mutableUseFrameRateApi() = true;
-
     setupScheduler();
 
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-    mFlinger.mutableEventQueue().reset(mMessageQueue);
 }
 
 void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
@@ -184,11 +169,9 @@
 }
 
 namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
 TEST_P(SetFrameRateTest, SetAndGet) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -199,7 +182,7 @@
 }
 
 TEST_P(SetFrameRateTest, SetAndGetParent) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -224,7 +207,7 @@
 }
 
 TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -263,7 +246,7 @@
 }
 
 TEST_P(SetFrameRateTest, SetAndGetChild) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -288,7 +271,7 @@
 }
 
 TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -327,7 +310,7 @@
 }
 
 TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -357,7 +340,7 @@
 }
 
 TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -388,7 +371,7 @@
 }
 
 TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
@@ -417,8 +400,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
-                         testing::Values(std::make_shared<BufferQueueLayerFactory>(),
-                                         std::make_shared<BufferStateLayerFactory>(),
+                         testing::Values(std::make_shared<BufferStateLayerFactory>(),
                                          std::make_shared<EffectLayerFactory>()),
                          PrintToStringParamName);
 
@@ -471,24 +453,20 @@
     parent->setFrameRate(FRAME_RATE_VOTE1);
     commitTransaction();
 
-    mFlinger.mutableScheduler()
-            .mutableLayerHistory()
-            ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
-    mFlinger.mutableScheduler()
-            .mutableLayerHistory()
-            ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+    auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
+    history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+    history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
 
-    const auto layerHistorySummary =
-            mFlinger.mutableScheduler()
-                    .mutableLayerHistory()
-                    ->summarize(*mFlinger.mutableScheduler().refreshRateConfigs(), 0);
-    ASSERT_EQ(2u, layerHistorySummary.size());
-    EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[0].desiredRefreshRate));
-    EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[1].desiredRefreshRate));
+    const auto configs = mFlinger.mutableScheduler().refreshRateConfigs();
+    const auto summary = history.summarize(*configs, 0);
+
+    ASSERT_EQ(2u, summary.size());
+    EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate);
+    EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[1].desiredRefreshRate);
 }
 
 TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     const auto& layerFactory = GetParam();
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 2362a31..f7d34ac 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -51,8 +51,8 @@
     // --------------------------------------------------------------------
     // Cleanup conditions
 
-    // Destroying the display invalidates the display state.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    // Creating the display commits a display transaction.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 }
 
 TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
@@ -86,9 +86,9 @@
     // --------------------------------------------------------------------
     // Cleanup conditions
 
-    // Destroying the display invalidates the display state.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    // Creating the display commits a display transaction.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 }
 
 } // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index e2be074..c7e61c9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -40,8 +40,8 @@
     // The call should notify the interceptor that a display was created.
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
 
-    // Destroying the display invalidates the display state.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    // Destroying the display commits a display transaction.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     // --------------------------------------------------------------------
     // Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
new file mode 100644
index 0000000..56a0506
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/MockEventThread.h"
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <scheduler/Fps.h>
+
+namespace android {
+namespace {
+
+using android::hardware::graphics::composer::V2_4::Error;
+using android::hardware::graphics::composer::V2_4::VsyncPeriodChangeTimeline;
+
+class DisplayModeSwitchingTest : public DisplayTransactionTest {
+public:
+    void SetUp() override {
+        injectFakeBufferQueueFactory();
+        injectFakeNativeWindowSurfaceFactory();
+
+        PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this);
+        PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this);
+        PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this);
+        PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
+        PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
+
+        mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+
+        mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+                           .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120,
+                                               kDisplayMode90DifferentResolution})
+                           .setActiveMode(kDisplayModeId60)
+                           .inject();
+
+        setupScheduler();
+
+        // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
+        // will call setActiveConfig instead of setActiveConfigWithConstraints.
+        ON_CALL(*mComposer, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+                .WillByDefault(Return(true));
+    }
+
+protected:
+    void setupScheduler();
+    void testChangeRefreshRate(bool isDisplayActive, bool isRefreshRequired);
+
+    sp<DisplayDevice> mDisplay;
+    mock::EventThread* mAppEventThread;
+
+    const DisplayModeId kDisplayModeId60 = DisplayModeId(0);
+    const DisplayModePtr kDisplayMode60 =
+            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value()))
+                    .setId(kDisplayModeId60)
+                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+                    .setVsyncPeriod((60_Hz).getPeriodNsecs())
+                    .setGroup(0)
+                    .setHeight(1000)
+                    .setWidth(1000)
+                    .build();
+
+    const DisplayModeId kDisplayModeId90 = DisplayModeId(1);
+    const DisplayModePtr kDisplayMode90 =
+            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value()))
+                    .setId(kDisplayModeId90)
+                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+                    .setVsyncPeriod((90_Hz).getPeriodNsecs())
+                    .setGroup(1)
+                    .setHeight(1000)
+                    .setWidth(1000)
+                    .build();
+
+    const DisplayModeId kDisplayModeId120 = DisplayModeId(2);
+    const DisplayModePtr kDisplayMode120 =
+            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value()))
+                    .setId(kDisplayModeId120)
+                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+                    .setVsyncPeriod((120_Hz).getPeriodNsecs())
+                    .setGroup(2)
+                    .setHeight(1000)
+                    .setWidth(1000)
+                    .build();
+
+    const DisplayModeId kDisplayModeId90DifferentResolution = DisplayModeId(3);
+    const DisplayModePtr kDisplayMode90DifferentResolution =
+            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90DifferentResolution.value()))
+                    .setId(kDisplayModeId90DifferentResolution)
+                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+                    .setVsyncPeriod((90_Hz).getPeriodNsecs())
+                    .setGroup(3)
+                    .setHeight(2000)
+                    .setWidth(2000)
+                    .build();
+};
+
+void DisplayModeSwitchingTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    mAppEventThread = eventThread.get();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
+            .WillRepeatedly(
+                    Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread), /*callback*/ nullptr,
+                            /*hasMultipleModes*/ true);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+    mFlinger.onActiveDisplayChanged(mDisplay);
+
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+
+    ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+    // Verify that next commit will call setActiveConfigWithConstraints in HWC
+    const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+    EXPECT_CALL(*mComposer,
+                setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+                                               hal::HWConfigId(kDisplayModeId90.value()), _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+    mFlinger.commit();
+
+    Mock::VerifyAndClearExpectations(mComposer);
+    ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+    // Verify that the next commit will complete the mode change and send
+    // a onModeChanged event to the framework.
+
+    EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+    mFlinger.commit();
+    Mock::VerifyAndClearExpectations(mAppEventThread);
+
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+
+    mFlinger.onActiveDisplayChanged(mDisplay);
+
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        kDisplayModeId90.value(), true, 0.f, 120.f, 0.f, 120.f);
+
+    ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+    // Verify that next commit will call setActiveConfigWithConstraints in HWC
+    // and complete the mode change.
+    const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+    EXPECT_CALL(*mComposer,
+                setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+                                               hal::HWConfigId(kDisplayModeId90.value()), _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+    EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+
+    mFlinger.commit();
+
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+}
+
+TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
+    // Test that if we call setDesiredDisplayModeSpecs while a previous mode change
+    // is still being processed the later call will be respected.
+
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+    mFlinger.onActiveDisplayChanged(mDisplay);
+
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+
+    const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+    EXPECT_CALL(*mComposer,
+                setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+                                               hal::HWConfigId(kDisplayModeId90.value()), _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+    mFlinger.commit();
+
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        kDisplayModeId120.value(), false, 0.f, 180.f, 0.f, 180.f);
+
+    ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+
+    EXPECT_CALL(*mComposer,
+                setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+                                               hal::HWConfigId(kDisplayModeId120.value()), _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+    mFlinger.commit();
+
+    ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+
+    mFlinger.commit();
+
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId120);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+    mFlinger.onActiveDisplayChanged(mDisplay);
+
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        kDisplayModeId90DifferentResolution.value(), false, 0.f,
+                                        120.f, 0.f, 120.f);
+
+    ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90DifferentResolution);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+    // Verify that next commit will call setActiveConfigWithConstraints in HWC
+    // and complete the mode change.
+    const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+    EXPECT_CALL(*mComposer,
+                setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+                                               hal::HWConfigId(
+                                                       kDisplayModeId90DifferentResolution.value()),
+                                               _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+    EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true));
+
+    // Misc expecations. We don't need to enforce these method calls, but since the helper methods
+    // already set expectations we should add new ones here, otherwise the test will fail.
+    EXPECT_CALL(*mConsumer, setDefaultBufferSize(2000, 2000)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(hal::Error::NONE));
+
+    // Create a new native surface to be used by the recreated display.
+    mNativeWindowSurface = nullptr;
+    injectFakeNativeWindowSurfaceFactory();
+    PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
+
+    const auto displayToken = mDisplay->getDisplayToken().promote();
+
+    mFlinger.commit();
+
+    // The DisplayDevice will be destroyed and recreated,
+    // so we need to update with the new instance.
+    mDisplay = mFlinger.getDisplay(displayToken);
+
+    ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90DifferentResolution);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
similarity index 74%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index b713334..6959ee3 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -22,8 +22,7 @@
 namespace android {
 namespace {
 
-class HandleTransactionLockedTest : public DisplayTransactionTest {
-public:
+struct DisplayTransactionCommitTest : DisplayTransactionTest {
     template <typename Case>
     void setupCommonPreconditions();
 
@@ -55,7 +54,7 @@
 };
 
 template <typename Case>
-void HandleTransactionLockedTest::setupCommonPreconditions() {
+void DisplayTransactionCommitTest::setupCommonPreconditions() {
     // Wide color displays support is configured appropriately
     Case::WideColorSupport::injectConfigChange(this);
 
@@ -68,7 +67,7 @@
 }
 
 template <typename Case, bool connected>
-void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) {
     const auto convert = [](auto physicalDisplayId) {
         return std::make_optional(DisplayId{physicalDisplayId});
     };
@@ -79,7 +78,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
+void DisplayTransactionCommitTest::setupCommonCallExpectationsForConnectProcessing() {
     Case::Display::setupHwcHotplugCallExpectations(this);
 
     Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
@@ -97,7 +96,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
+void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
 
     expectHotplugReceived<Case, false>(mEventThread);
@@ -105,7 +104,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
+void DisplayTransactionCommitTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
     // The display device should have been set up in the list of displays.
     ASSERT_TRUE(hasDisplayDevice(displayToken));
     const auto& device = getDisplayDevice(displayToken);
@@ -137,7 +136,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
+void DisplayTransactionCommitTest::verifyPhysicalDisplayIsConnected() {
     // HWComposer should have an entry for the display
     EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
@@ -150,14 +149,14 @@
     verifyDisplayIsConnected<Case>(displayToken);
 }
 
-void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
+void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
     EXPECT_FALSE(hasDisplayDevice(displayToken));
     EXPECT_FALSE(hasCurrentDisplayState(displayToken));
     EXPECT_FALSE(hasDrawingDisplayState(displayToken));
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::processesHotplugConnectCommon() {
+void DisplayTransactionCommitTest::processesHotplugConnectCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
@@ -174,7 +173,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -191,7 +190,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
+void DisplayTransactionCommitTest::ignoresHotplugConnectCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
@@ -203,7 +202,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -213,7 +212,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
+void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
@@ -238,7 +237,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -255,18 +254,18 @@
     verifyDisplayIsNotConnected(existing.token());
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectPrimaryDisplay) {
     processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectExternalDisplay) {
     // Inject a primary display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
 
     processesHotplugConnectCommon<SimpleExternalDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
+TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
     // Inject both a primary and external display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
     ExternalDisplayVariant::injectHwcDisplay(this);
@@ -281,108 +280,119 @@
     ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
-    processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectPrimaryDisplay) {
+    EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(),
+                testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectExternalDisplay) {
     processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimary) {
+    EXPECT_EXIT(
+            [this] {
+                using Case = SimplePrimaryDisplayCase;
 
-    // --------------------------------------------------------------------
-    // Preconditions
+                // --------------------------------------------------------------------
+                // Preconditions
 
-    setupCommonPreconditions<Case>();
+                setupCommonPreconditions<Case>();
 
-    // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-    // A hotplug disconnect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+                // A hotplug connect event is enqueued for a display
+                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+                // A hotplug disconnect event is also enqueued for the same display
+                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
 
-    // --------------------------------------------------------------------
-    // Call Expectations
+                // --------------------------------------------------------------------
+                // Call Expectations
 
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+                setupCommonCallExpectationsForConnectProcessing<Case>();
+                setupCommonCallExpectationsForDisconnectProcessing<Case>();
 
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+                EXPECT_CALL(*mComposer,
+                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+                                            IComposerClient::Vsync::DISABLE))
+                        .WillOnce(Return(Error::NONE));
+                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
 
-    // --------------------------------------------------------------------
-    // Invocation
+                // --------------------------------------------------------------------
+                // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
-    // --------------------------------------------------------------------
-    // Postconditions
+                // --------------------------------------------------------------------
+                // Postconditions
 
-    // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+                // HWComposer should not have an entry for the display
+                EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // SF should not have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+                // SF should not have a display token.
+                const auto displayId = Case::Display::DISPLAY_ID::get();
+                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+            }(),
+            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimary) {
+    EXPECT_EXIT(
+            [this] {
+                using Case = SimplePrimaryDisplayCase;
 
-    // --------------------------------------------------------------------
-    // Preconditions
+                // --------------------------------------------------------------------
+                // Preconditions
 
-    setupCommonPreconditions<Case>();
+                setupCommonPreconditions<Case>();
 
-    // The display is already completely set up.
-    Case::Display::injectHwcDisplay(this);
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
+                // The display is already completely set up.
+                Case::Display::injectHwcDisplay(this);
+                auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+                existing.inject();
 
-    // A hotplug disconnect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-    // A hotplug connect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+                // A hotplug disconnect event is enqueued for a display
+                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+                // A hotplug connect event is also enqueued for the same display
+                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
 
-    // --------------------------------------------------------------------
-    // Call Expectations
+                // --------------------------------------------------------------------
+                // Call Expectations
 
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+                setupCommonCallExpectationsForConnectProcessing<Case>();
+                setupCommonCallExpectationsForDisconnectProcessing<Case>();
 
-    // --------------------------------------------------------------------
-    // Invocation
+                // --------------------------------------------------------------------
+                // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
-    // --------------------------------------------------------------------
-    // Postconditions
+                // --------------------------------------------------------------------
+                // Postconditions
 
-    // The existing token should have been removed
-    verifyDisplayIsNotConnected(existing.token());
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
-    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+                // The existing token should have been removed
+                verifyDisplayIsNotConnected(existing.token());
+                const auto displayId = Case::Display::DISPLAY_ID::get();
+                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+                EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
 
-    // A new display should be connected in its place
+                // A new display should be connected in its place
 
-    verifyPhysicalDisplayIsConnected<Case>();
+                verifyPhysicalDisplayIsConnected<Case>();
 
-    // --------------------------------------------------------------------
-    // Cleanup conditions
+                // --------------------------------------------------------------------
+                // Cleanup conditions
 
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+                EXPECT_CALL(*mComposer,
+                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+                                            IComposerClient::Vsync::DISABLE))
+                        .WillOnce(Return(Error::NONE));
+                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+            }(),
+            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAdded) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
@@ -433,7 +443,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -453,7 +463,7 @@
     mFlinger.mutableDrawingState().displays.removeItem(displayToken);
 }
 
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAddedWithNoSurface) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
@@ -479,7 +489,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -493,7 +503,7 @@
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
 }
 
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayRemoval) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
@@ -511,7 +521,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -520,11 +530,11 @@
     verifyDisplayIsNotConnected(existing.token());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
-    constexpr uint32_t oldLayerStack = 0u;
-    constexpr uint32_t newLayerStack = 123u;
+    constexpr ui::LayerStack oldLayerStack = ui::DEFAULT_LAYER_STACK;
+    constexpr ui::LayerStack newLayerStack{123u};
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -540,7 +550,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -548,7 +558,7 @@
     EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayTransformChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr ui::Rotation oldTransform = ui::ROTATION_0;
@@ -568,7 +578,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -576,7 +586,7 @@
     EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     const Rect oldLayerStackRect(0, 0, 0, 0);
@@ -596,7 +606,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -604,7 +614,7 @@
     EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     const Rect oldDisplayRect(0, 0);
@@ -624,7 +634,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -632,7 +642,7 @@
     EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayWidthChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr int oldWidth = 0;
@@ -674,10 +684,10 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayHeightChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr int oldWidth = 0;
@@ -719,10 +729,10 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr uint32_t kOldWidth = 567;
@@ -769,7 +779,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize);
     EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index bd89397..c9a2b00 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -38,9 +38,8 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // We expect invalidate() to be invoked once to trigger display transaction
-    // processing.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    // We expect a scheduled commit for the display transaction.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     // --------------------------------------------------------------------
     // Invocation
@@ -86,9 +85,8 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // We expect invalidate() to be invoked once to trigger display transaction
-    // processing.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    // We expect a scheduled commit for the display transaction.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     // --------------------------------------------------------------------
     // Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index 69e0501..ec7e8a7 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -17,6 +17,9 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <chrono>
+#include <thread>
+
 #include "DisplayTransactionTestHelpers.h"
 
 #include <android/hardware/power/Boost.h>
@@ -27,6 +30,8 @@
 using android::hardware::power::Boost;
 
 TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+    using namespace std::chrono_literals;
+
     mFlinger.scheduler()->replaceTouchTimer(100);
     std::this_thread::sleep_for(10ms);                  // wait for callback to be triggered
     EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
@@ -47,4 +52,4 @@
 }
 
 } // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index ef8b149..37cf05e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -46,9 +46,8 @@
     // We expect a call to get the active display config.
     Case::Display::setupHwcGetActiveConfigCallExpectations(this);
 
-    // We expect invalidate() to be invoked once to trigger display transaction
-    // processing.
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    // We expect a scheduled commit for the display transaction.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
 
@@ -63,15 +62,11 @@
     // The primary display should have a current state
     ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
     const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
-    // The layer stack state should be set to zero
-    EXPECT_EQ(0u, primaryDisplayState.layerStack);
-    // The orientation state should be set to zero
+
+    // The primary display state should be reset
+    EXPECT_EQ(ui::DEFAULT_LAYER_STACK, primaryDisplayState.layerStack);
     EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
-
-    // The orientedDisplaySpaceRect state should be set to INVALID
     EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
-
-    // The layerStackSpaceRect state should be set to INVALID
     EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
 
     // The width and height should both be zero
@@ -99,4 +94,4 @@
 }
 
 } // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
index fc40818..7d9e22b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -25,6 +25,8 @@
 namespace android {
 namespace {
 
+constexpr ui::LayerStack LAYER_STACK{456u};
+
 class SetDisplayStateLockedTest : public DisplayTransactionTest {};
 
 TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
@@ -38,7 +40,7 @@
     DisplayState state;
     state.what = DisplayState::eLayerStackChanged;
     state.token = displayToken;
-    state.layerStack = 456;
+    state.layerStack = LAYER_STACK;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -167,13 +169,13 @@
     display.inject();
 
     // The display has a layer stack set
-    display.mutableCurrentDisplayState().layerStack = 456u;
+    display.mutableCurrentDisplayState().layerStack = LAYER_STACK;
 
     // The incoming request sets the same layer stack
     DisplayState state;
     state.what = DisplayState::eLayerStackChanged;
     state.token = display.token();
-    state.layerStack = 456u;
+    state.layerStack = LAYER_STACK;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -187,7 +189,7 @@
     EXPECT_EQ(0u, flags);
 
     // The current display state is unchanged
-    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+    EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack);
 }
 
 TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
@@ -201,13 +203,13 @@
     display.inject();
 
     // The display has a layer stack set
-    display.mutableCurrentDisplayState().layerStack = 654u;
+    display.mutableCurrentDisplayState().layerStack = ui::LayerStack{LAYER_STACK.id + 1};
 
     // The incoming request sets a different layer stack
     DisplayState state;
     state.what = DisplayState::eLayerStackChanged;
     state.token = display.token();
-    state.layerStack = 456u;
+    state.layerStack = LAYER_STACK;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -221,7 +223,7 @@
     EXPECT_EQ(eDisplayTransactionNeeded, flags);
 
     // The desired display state has been set to the new value.
-    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+    EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack);
 }
 
 TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfFlagsNotChanged) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index eea1002..b57feff 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -273,7 +273,7 @@
     }
 
     static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
     }
 
     static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index e32c4bf..38dceb9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -34,7 +34,6 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = true;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableUseColorManagement() = true;
         test->mFlinger.mutableHasWideColorDisplay() = true;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
@@ -257,6 +256,7 @@
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
+    state.flags = Case::Display::DISPLAY_FLAGS;
 
     auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
                                                          displaySurface, producer);
@@ -279,6 +279,8 @@
     EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
     EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
               device->getSupportedPerFrameMetadata());
+    EXPECT_EQ(Case::Display::DISPLAY_FLAGS & DisplayDevice::eReceivesInput,
+              device->receivesInput());
 
     if constexpr (Case::Display::CONNECTION_TYPE::value) {
         EXPECT_EQ(1, device->getSupportedModes().size());
@@ -292,7 +294,9 @@
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
-    setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+    // External displays must be secondary, as the primary display cannot be disconnected.
+    EXPECT_EXIT(setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(),
+                testing::KilledBySignal(SIGABRT), "Missing primary display");
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1d21bd4..364d8f1 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -28,23 +28,30 @@
 #include "mock/MockVSyncTracker.h"
 #include "mock/MockVsyncController.h"
 
-namespace android {
+namespace android::scheduler {
 
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ICompositor {
 public:
-    TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
-                      ISchedulerCallback& callback)
+    TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
-                              std::make_unique<mock::VSyncTracker>(), refreshRateConfigs,
+                              std::make_unique<mock::VSyncTracker>(), std::move(configs),
                               callback) {}
 
-    TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
-                      std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
-                      const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
-                      ISchedulerCallback& callback)
-          : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr},
-                      refreshRateConfigs, callback, createLayerHistory(),
-                      {.useContentDetection = true}) {}
+    TestableScheduler(std::unique_ptr<VsyncController> controller,
+                      std::unique_ptr<VSyncTracker> tracker,
+                      std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+          : Scheduler(*this, callback, Feature::kContentDetection) {
+        mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
+        setRefreshRateConfigs(std::move(configs));
+
+        ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
+            // Execute task to prevent broken promise exception on destruction.
+            handler->handleMessage(Message());
+        });
+    }
+
+    MOCK_METHOD(void, scheduleFrame, (), (override));
+    MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -58,20 +65,16 @@
     auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
     auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
 
-    bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
-
-    auto* mutableLayerHistory() { return mLayerHistory.get(); }
+    auto& mutableLayerHistory() { return mLayerHistory; }
 
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
-        if (!mLayerHistory) return 0;
-        return mutableLayerHistory()->mLayerInfos.size();
+        return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
     }
 
     auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
 
     size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
-        if (!mLayerHistory) return 0;
-        return mutableLayerHistory()->mActiveLayersEnd;
+        return mLayerHistory.mActiveLayerInfos.size();
     }
 
     void replaceTouchTimer(int64_t millis) {
@@ -86,32 +89,29 @@
     }
 
     bool isTouchActive() {
-        std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        return mFeatures.touch == Scheduler::TouchState::Active;
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        return mPolicy.touch == Scheduler::TouchState::Active;
     }
 
     void dispatchCachedReportedMode() {
-        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        std::lock_guard<std::mutex> lock(mPolicyLock);
         return Scheduler::dispatchCachedReportedMode();
     }
 
-    void clearOptionalFieldsInFeatures() {
-        std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        mFeatures.cachedModeChangedParams.reset();
+    void clearCachedReportedMode() {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.cachedModeChangedParams.reset();
     }
 
     void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
         return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
-    ~TestableScheduler() {
-        // All these pointer and container clears help ensure that GMock does
-        // not report a leaked object, since the Scheduler instance may
-        // still be referenced by something despite our best efforts to destroy
-        // it after each test is done.
-        mVsyncSchedule.controller.reset();
-        mConnections.clear();
-    }
+private:
+    // ICompositor overrides:
+    bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
+    void composite(nsecs_t) override {}
+    void sample() override {}
 };
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a23361e..361d629 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -24,6 +24,7 @@
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/DisplaySurface.h>
 #include <gui/ScreenCaptureResults.h>
+#include <algorithm>
 
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
@@ -73,20 +74,11 @@
         return nullptr;
     }
 
-    std::unique_ptr<MessageQueue> createMessageQueue() override {
-        return std::make_unique<android::impl::MessageQueue>();
-    }
-
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps /*currentRefreshRate*/) override {
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    std::unique_ptr<Scheduler> createScheduler(
-            const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override {
-        return nullptr;
-    }
-
     sp<SurfaceInterceptor> createSurfaceInterceptor() override {
         return new android::impl::SurfaceInterceptor();
     }
@@ -178,12 +170,12 @@
 
 } // namespace surfaceflinger::test
 
-class TestableSurfaceFlinger final : private ISchedulerCallback {
+class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback {
 public:
     using HotplugEvent = SurfaceFlinger::HotplugEvent;
 
     SurfaceFlinger* flinger() { return mFlinger.get(); }
-    TestableScheduler* scheduler() { return mScheduler; }
+    scheduler::TestableScheduler* scheduler() { return mScheduler; }
 
     // Extend this as needed for accessing SurfaceFlinger private (and public)
     // functions.
@@ -206,10 +198,11 @@
                         std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
-                        ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) {
+                        scheduler::ISchedulerCallback* callback = nullptr,
+                        bool hasMultipleModes = false) {
         DisplayModes modes{DisplayMode::Builder(0)
                                    .setId(DisplayModeId(0))
-                                   .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                   .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                                    .setVsyncPeriod(16'666'667)
                                    .setGroup(0)
                                    .build()};
@@ -217,7 +210,7 @@
         if (hasMultipleModes) {
             modes.emplace_back(DisplayMode::Builder(1)
                                        .setId(DisplayModeId(1))
-                                       .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                       .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                                        .setVsyncPeriod(11'111'111)
                                        .setGroup(0)
                                        .build());
@@ -233,17 +226,18 @@
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
                                                               /*powerMode=*/hal::PowerMode::OFF);
 
-        mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                           mRefreshRateConfigs, *(callback ?: this));
+        mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
+                                                      std::move(vsyncTracker), mRefreshRateConfigs,
+                                                      *(callback ?: this));
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
         resetScheduler(mScheduler);
     }
 
-    void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+    void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
 
-    TestableScheduler& mutableScheduler() const { return *mScheduler; }
+    scheduler::TestableScheduler& mutableScheduler() const { return *mScheduler; }
 
     using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
@@ -276,7 +270,8 @@
         layer->editCompositionState()->sidebandStream = sidebandStream;
     }
 
-    void setLayerCompositionType(const sp<Layer>& layer, hal::Composition type) {
+    void setLayerCompositionType(const sp<Layer>& layer,
+                                 aidl::android::hardware::graphics::composer3::Composition type) {
         auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
         LOG_ALWAYS_FATAL_IF(!outputLayer);
         auto& state = outputLayer->editState();
@@ -296,6 +291,16 @@
      * Forwarding for functions being tested
      */
 
+    nsecs_t commit() {
+        constexpr int64_t kVsyncId = 123;
+        const nsecs_t now = systemTime();
+        const nsecs_t expectedVsyncTime = now + 10'000'000;
+        mFlinger->commit(now, kVsyncId, expectedVsyncTime);
+        return now;
+    }
+
+    void commitAndComposite() { mFlinger->composite(commit()); }
+
     auto createDisplay(const String8& displayName, bool secure) {
         return mFlinger->createDisplay(displayName, secure);
     }
@@ -304,6 +309,11 @@
         return mFlinger->destroyDisplay(displayToken);
     }
 
+    auto getDisplay(const sp<IBinder>& displayToken) {
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        return mFlinger->getDisplayDeviceLocked(displayToken);
+    }
+
     void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
 
     auto setupNewDisplayDeviceInternal(
@@ -316,9 +326,9 @@
                                                        dispSurface, producer);
     }
 
-    auto handleTransactionLocked(uint32_t transactionFlags) {
-        Mutex::Autolock _l(mFlinger->mStateLock);
-        return mFlinger->handleTransactionLocked(transactionFlags);
+    auto commitTransactionsLocked(uint32_t transactionFlags) {
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        return mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
     void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
@@ -326,7 +336,7 @@
     }
 
     auto setDisplayStateLocked(const DisplayState& s) {
-        Mutex::Autolock _l(mFlinger->mStateLock);
+        Mutex::Autolock lock(mFlinger->mStateLock);
         return mFlinger->setDisplayStateLocked(s);
     }
 
@@ -337,16 +347,17 @@
 
     auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
 
+    auto setDisplayBrightness(const sp<IBinder>& display,
+                              const gui::DisplayBrightness& brightness) {
+        return mFlinger->setDisplayBrightness(display, brightness);
+    }
+
     // Allow reading display state without locking, as if called on the SF main thread.
     auto setPowerModeInternal(const sp<DisplayDevice>& display,
                               hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto onMessageReceived(int32_t what) {
-        return mFlinger->onMessageReceived(what, /*vsyncId=*/0, systemTime());
-    }
-
     auto renderScreenImplLocked(const RenderArea& renderArea,
                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,
                                 const std::shared_ptr<renderengine::ExternalTexture>& buffer,
@@ -369,6 +380,7 @@
 
     auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
     auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+    auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
 
     auto setTransactionState(
             const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
@@ -382,7 +394,7 @@
                                              listenerCallbacks, transactionId);
     }
 
-    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); };
 
     auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
         return mFlinger->onTransact(code, data, reply, flags);
@@ -395,6 +407,21 @@
         return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
     }
 
+    auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
+                                    bool allowGroupSwitching, float primaryRefreshRateMin,
+                                    float primaryRefreshRateMax, float appRequestRefreshRateMin,
+                                    float appRequestRefreshRateMax) {
+        return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
+                                                    primaryRefreshRateMin, primaryRefreshRateMax,
+                                                    appRequestRefreshRateMin,
+                                                    appRequestRefreshRateMax);
+    }
+
+    void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -419,14 +446,12 @@
      */
 
     auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
-    auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; }
 
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
     auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
     auto& mutableDisplays() { return mFlinger->mDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
-    auto& mutableEventQueue() { return mFlinger->mEventQueue; }
-    auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
+    auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
     auto& mutableInterceptor() { return mFlinger->mInterceptor; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
     auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
@@ -439,9 +464,7 @@
 
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
-    auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
-    auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
-    auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
+    auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
     auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
 
     auto fromHandle(const sp<IBinder>& handle) {
@@ -456,7 +479,6 @@
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
-        mutableEventQueue().reset();
         mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
@@ -487,7 +509,7 @@
         static constexpr hal::HWDisplayId DEFAULT_HWC_DISPLAY_ID = 1000;
         static constexpr int32_t DEFAULT_WIDTH = 1920;
         static constexpr int32_t DEFAULT_HEIGHT = 1280;
-        static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'666;
+        static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667;
         static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
         static constexpr int32_t DEFAULT_DPI = 320;
         static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
@@ -601,13 +623,12 @@
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
                 if (mIsPrimary) {
-                    flinger->mutableInternalHwcDisplayId() = mHwcDisplayId;
+                    flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId;
                 } else {
-                    // If there is an external HWC display there should always be an internal ID
+                    // If there is an external HWC display, there should always be a primary ID
                     // as well. Set it to some arbitrary value.
-                    auto& internalId = flinger->mutableInternalHwcDisplayId();
-                    if (!internalId) internalId = mHwcDisplayId - 1;
-                    flinger->mutableExternalHwcDisplayId() = mHwcDisplayId;
+                    auto& primaryId = flinger->mutablePrimaryHwcDisplayId();
+                    if (!primaryId) primaryId = mHwcDisplayId - 1;
                 }
             }
         }
@@ -642,11 +663,11 @@
             mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
 
-            mActiveModeId = DisplayModeId(0);
+            mCreationArgs.activeModeId = DisplayModeId(0);
             DisplayModePtr activeMode =
                     DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
-                            .setId(mActiveModeId)
-                            .setPhysicalDisplayId(PhysicalDisplayId(0))
+                            .setId(mCreationArgs.activeModeId)
+                            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                             .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
                             .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
                             .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
@@ -681,7 +702,7 @@
         auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
 
         auto& setActiveMode(DisplayModeId mode) {
-            mActiveModeId = mode;
+            mCreationArgs.activeModeId = mode;
             return *this;
         }
 
@@ -736,14 +757,29 @@
                 const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
-                state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
+
+                const DisplayModePtr activeModePtr =
+                        *std::find_if(mCreationArgs.supportedModes.begin(),
+                                      mCreationArgs.supportedModes.end(), [&](DisplayModePtr mode) {
+                                          return mode->getId() == mCreationArgs.activeModeId;
+                                      });
+                state.physical = {.id = *physicalId,
+                                  .type = *type,
+                                  .hwcDisplayId = *mHwcDisplayId,
+                                  .deviceProductInfo = {},
+                                  .supportedModes = mCreationArgs.supportedModes,
+                                  .activeMode = activeModePtr};
             }
 
             state.isSecure = mCreationArgs.isSecure;
 
+            mCreationArgs.refreshRateConfigs =
+                    std::make_shared<scheduler::RefreshRateConfigs>(mCreationArgs.supportedModes,
+                                                                    mCreationArgs.activeModeId);
+
             sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
             if (!device->isVirtual()) {
-                device->setActiveMode(mActiveModeId);
+                device->setActiveMode(mCreationArgs.activeModeId);
             }
             mFlinger.mutableDisplays().emplace(mDisplayToken, device);
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
@@ -761,19 +797,18 @@
         sp<BBinder> mDisplayToken = new BBinder();
         DisplayDeviceCreationArgs mCreationArgs;
         const std::optional<hal::HWDisplayId> mHwcDisplayId;
-        DisplayModeId mActiveModeId;
     };
 
 private:
+    void scheduleComposite(FrameHint) override {}
     void setVsyncEnabled(bool) override {}
-    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {}
-    void repaintEverythingForHWC() override {}
+    void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() {}
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
-    TestableScheduler* mScheduler = nullptr;
+    scheduler::TestableScheduler* mScheduler = nullptr;
     std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 317cdf1..0ef8456 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -64,17 +64,15 @@
 #define LAYER_ID_0            0
 #define LAYER_ID_1            1
 #define UID_0                 123
-#define REFRESH_RATE_0        61
-#define RENDER_RATE_0         31
 #define REFRESH_RATE_BUCKET_0 60
 #define RENDER_RATE_BUCKET_0  30
 #define LAYER_ID_INVALID      -1
 #define NUM_LAYERS            1
 #define NUM_LAYERS_INVALID    "INVALID"
 
-const constexpr Fps kRefreshRate0 = Fps(static_cast<float>(REFRESH_RATE_0));
-const constexpr Fps kRenderRate0 = Fps(static_cast<float>(RENDER_RATE_0));
-static constexpr int32_t kGameMode = TimeStatsHelper::GameModeUnsupported;
+constexpr Fps kRefreshRate0 = 61_Hz;
+constexpr Fps kRenderRate0 = 31_Hz;
+constexpr GameMode kGameMode = GameMode::Unsupported;
 
 enum InputCommand : int32_t {
     ENABLE                 = 0,
@@ -147,14 +145,14 @@
     std::string inputCommand(InputCommand cmd, bool useProto);
 
     void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
-                      TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode);
+                      TimeStats::SetFrameRateVote, GameMode);
 
     int32_t genRandomInt32(int32_t begin, int32_t end);
 
     template <size_t N>
     void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
                           nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {},
-                          int32_t gameMode = kGameMode) {
+                          GameMode gameMode = kGameMode) {
         for (size_t i = 0; i < N; i++, ts += 1000000) {
             setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote, gameMode);
         }
@@ -205,7 +203,7 @@
 }
 
 void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
-                                 TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode) {
+                                 TimeStats::SetFrameRateVote frameRateVote, GameMode gameMode) {
     switch (type) {
         case TimeStamp::POST:
             ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id),
@@ -1170,8 +1168,7 @@
     constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
-                     TimeStatsHelper::GameModeStandard);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard);
     for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
         mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
     }
@@ -1184,42 +1181,39 @@
                     TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
             .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
     };
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60,
-                     TimeStatsHelper::GameModeStandard);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60, GameMode::Standard);
 
-    mTimeStats->incrementJankyFrames(
-            {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-             TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerCpuDeadlineMissed,
-             DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
-    mTimeStats->incrementJankyFrames(
-            {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-             TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerGpuDeadlineMissed,
-             DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      TimeStatsHelper::GameModeStandard, JankType::DisplayHAL,
+                                      GameMode::Standard, JankType::SurfaceFlingerCpuDeadlineMissed,
                                       DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
                                       APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      TimeStatsHelper::GameModeStandard,
-                                      JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+                                      GameMode::Standard, JankType::SurfaceFlingerGpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_3MS});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      GameMode::Standard, JankType::DisplayHAL,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_3MS});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      GameMode::Standard, JankType::AppDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_3MS});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      GameMode::Standard, JankType::SurfaceFlingerScheduling,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_2MS});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      GameMode::Standard, JankType::PredictionError,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_2MS});
+    mTimeStats->incrementJankyFrames(
+            {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), GameMode::Standard,
+             JankType::AppDeadlineMissed | JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+             APP_DEADLINE_DELTA_2MS, APP_DEADLINE_DELTA_2MS});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
                                       DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
-    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      TimeStatsHelper::GameModeStandard,
-                                      JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
-                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
-    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      TimeStatsHelper::GameModeStandard, JankType::PredictionError,
-                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
-                                      APP_DEADLINE_DELTA_2MS});
-    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      TimeStatsHelper::GameModeStandard,
-                                      JankType::AppDeadlineMissed | JankType::BufferStuffing,
-                                      DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS,
-                                      APP_DEADLINE_DELTA_2MS});
-    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      TimeStatsHelper::GameModeStandard, JankType::None,
-                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
-                                      APP_DEADLINE_DELTA_3MS});
 
     std::string pulledData;
     EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
@@ -1295,22 +1289,18 @@
     constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
-                     TimeStatsHelper::GameModeStandard);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard);
     for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
         mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
     }
     for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
         mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
     }
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {},
-                     TimeStatsHelper::GameModeStandard);
 
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {},
-                     TimeStatsHelper::GameModePerformance);
-
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, TimeStatsHelper::GameModeBattery);
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, TimeStatsHelper::GameModeBattery);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {}, GameMode::Standard);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
 
     std::string pulledData;
     EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
@@ -1498,14 +1488,14 @@
         EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps;
     };
 
-    verifyRefreshRateBucket(Fps(91.f), 90);
-    verifyRefreshRateBucket(Fps(89.f), 90);
+    verifyRefreshRateBucket(91_Hz, 90);
+    verifyRefreshRateBucket(89_Hz, 90);
 
-    verifyRefreshRateBucket(Fps(61.f), 60);
-    verifyRefreshRateBucket(Fps(59.f), 60);
+    verifyRefreshRateBucket(61_Hz, 60);
+    verifyRefreshRateBucket(59_Hz, 60);
 
-    verifyRefreshRateBucket(Fps(31.f), 30);
-    verifyRefreshRateBucket(Fps(29.f), 30);
+    verifyRefreshRateBucket(31_Hz, 30);
+    verifyRefreshRateBucket(29_Hz, 30);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/TimerTest.cpp b/services/surfaceflinger/tests/unittests/TimerTest.cpp
index cda6bbf..0a3639d 100644
--- a/services/surfaceflinger/tests/unittests/TimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimerTest.cpp
@@ -26,11 +26,19 @@
 
 namespace android::scheduler {
 
+struct TestableTimer : public Timer {
+public:
+    void makeEpollError() {
+        // close the epoll file descriptor to cause an epoll error
+        close(mEpollFd);
+    }
+};
+
 struct TimerTest : testing::Test {
     static constexpr int mIterations = 20;
 
     AsyncCallRecorder<void (*)()> mCallbackRecorder;
-    Timer mTimer;
+    TestableTimer mTimer;
 
     void timerCallback() { mCallbackRecorder.recordCall(); }
 };
@@ -42,4 +50,14 @@
         EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
     }
 }
+
+TEST_F(TimerTest, recoversAfterEpollError) {
+    for (int i = 0; i < mIterations; i++) {
+        mTimer.makeEpollError();
+        mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00);
+        EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
+        EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
+    }
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 1a50427..16d4b59 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -24,12 +24,11 @@
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
+#include <ui/MockFence.h>
 #include <utils/String8.h>
 
-#include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
 #include "mock/MockVsyncController.h"
 
 namespace android {
@@ -46,7 +45,6 @@
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-        mFlinger.mutableEventQueue().reset(mMessageQueue);
         setupScheduler();
     }
 
@@ -74,20 +72,27 @@
         EXPECT_CALL(*mVSyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
 
+        EXPECT_CALL(*mFenceUnsignaled, getStatus())
+                .WillRepeatedly(Return(Fence::Status::Unsignaled));
+        EXPECT_CALL(*mFenceUnsignaled2, getStatus())
+                .WillRepeatedly(Return(Fence::Status::Unsignaled));
+        EXPECT_CALL(*mFenceSignaled, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+        EXPECT_CALL(*mFenceSignaled2, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
         mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
                                 std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
                                 std::move(eventThread), std::move(sfEventThread));
     }
 
-    TestableScheduler* mScheduler;
     TestableSurfaceFlinger mFlinger;
 
-    std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
-
-    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::VsyncController* mVsyncController = new mock::VsyncController();
     mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
+    mock::MockFence* mFenceUnsignaled = new mock::MockFence();
+    mock::MockFence* mFenceSignaled = new mock::MockFence();
+    mock::MockFence* mFenceUnsignaled2 = new mock::MockFence();
+    mock::MockFence* mFenceSignaled2 = new mock::MockFence();
 
     struct TransactionInfo {
         Vector<ComposerState> states;
@@ -103,7 +108,7 @@
         static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
     };
 
-    void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
+    void checkEqual(TransactionInfo info, TransactionState state) {
         EXPECT_EQ(0u, info.states.size());
         EXPECT_EQ(0u, state.states.size());
 
@@ -124,10 +129,18 @@
         transaction.frameTimelineInfo = frameTimelineInfo;
     }
 
+    void setupSingleWithComposer(TransactionInfo& transaction, uint32_t flags,
+                                 bool syncInputWindows, int64_t desiredPresentTime,
+                                 bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo,
+                                 const Vector<ComposerState>* states) {
+        setupSingle(transaction, flags, syncInputWindows, desiredPresentTime, isAutoTimestamp,
+                    frameTimelineInfo);
+        transaction.states = *states;
+    }
+
     void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
         ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-        // called in SurfaceFlinger::signalTransaction
-        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
                     /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -157,8 +170,7 @@
 
     void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
         ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-        // called in SurfaceFlinger::signalTransaction
-        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
         // first check will see desired present time has not passed,
         // but afterwards it will look like the desired present time has passed
@@ -187,12 +199,11 @@
 
     void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
         ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-        // called in SurfaceFlinger::signalTransaction
         nsecs_t time = systemTime();
         if (!syncInputWindows) {
-            EXPECT_CALL(*mMessageQueue, invalidate()).Times(2);
+            EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
         } else {
-            EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+            EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
         }
         // transaction that should go on the pending thread
         TransactionInfo transactionA;
@@ -248,6 +259,188 @@
         EXPECT_EQ(0u, transactionQueue.size());
     }
 
+    void Flush_removesUnsignaledFromTheQueue(Vector<ComposerState> state1,
+                                             Vector<ComposerState> state2,
+                                             bool updateApplyToken = true) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+
+        TransactionInfo transactionA;
+        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state1);
+
+        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
+
+        TransactionInfo transactionB;
+        if (updateApplyToken) {
+            transactionB.applyToken = sp<IBinder>();
+        }
+        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state2);
+        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    void Flush_removesFromTheQueue(const Vector<ComposerState>& state) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+        TransactionInfo transaction;
+        setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state);
+
+        mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                     transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(1u, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    void Flush_keepsInTheQueue(const Vector<ComposerState>& state) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+        TransactionInfo transaction;
+        setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state);
+
+        mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                     transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(1u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    void Flush_KeepsUnsignaledInTheQueue(const Vector<ComposerState>& state1,
+                                         const Vector<ComposerState>& state2,
+                                         bool updateApplyToken = true,
+                                         uint32_t pendingTransactionQueueSize = 1u) {
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        auto time = systemTime();
+        TransactionInfo transactionA;
+        TransactionInfo transactionB;
+        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state1);
+        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state2);
+        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
+        if (updateApplyToken) {
+            transactionB.applyToken = sp<IBinder>();
+        }
+        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(pendingTransactionQueueSize, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+    }
+
+    void Flush_removesSignaledFromTheQueue(const Vector<ComposerState>& state1,
+                                           const Vector<ComposerState>& state2) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+        auto time = systemTime();
+        TransactionInfo transactionA;
+        TransactionInfo transactionB;
+        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state1);
+        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state2);
+        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
+        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    static Vector<ComposerState> createComposerStateVector(const ComposerState& state1,
+                                                           const ComposerState& state2) {
+        Vector<ComposerState> states;
+        states.push_back(state1);
+        states.push_back(state2);
+        return states;
+    }
+
+    static Vector<ComposerState> createComposerStateVector(const ComposerState& state) {
+        Vector<ComposerState> states;
+        states.push_back(state);
+        return states;
+    }
+
+    static ComposerState createComposerState(int layerId, sp<Fence> fence,
+                                             uint32_t stateFlags = layer_state_t::eBufferChanged) {
+        ComposerState composer_state;
+        composer_state.state.bufferData.acquireFence = std::move(fence);
+        composer_state.state.layerId = layerId;
+        composer_state.state.bufferData.flags = BufferData::BufferDataChange::fenceChanged;
+        composer_state.state.flags = stateFlags;
+        return composer_state;
+    }
+
     bool mHasListenerCallbacks = false;
     std::vector<ListenerCallbacks> mCallbacks;
     int mTransactionNumber = 0;
@@ -255,8 +448,7 @@
 
 TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
     ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-    // called in SurfaceFlinger::signalTransaction
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     TransactionInfo transactionA; // transaction to go on pending queue
     setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
@@ -331,4 +523,216 @@
     auto ret = mFlinger.fromHandle(badHandle);
     EXPECT_EQ(nullptr, ret.promote().get());
 }
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleSignaledFromTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleUnSignaledFromTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(createComposerStateVector(
+            createComposerState(/*layerId*/ 1, mFenceUnsignaled, layer_state_t::eCropChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(createComposerStateVector(
+            createComposerState(/*layerId*/ 1, mFenceUnsignaled,
+                                layer_state_t::eCropChanged | layer_state_t::eBufferChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsInTheQueueSameApplyTokenMultiState_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_MultipleStateTransaction_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_removesSignaledFromTheQueue(createComposerStateVector(
+                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                      createComposerStateVector(
+                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemoveSignaledWithUnsignaledIntact_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsTransactionInTheQueueSameApplyToken_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceSignaled)),
+                                    /*updateApplyToken*/ false);
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsTransactionInTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+                                    /*updateApplyToken*/ true,
+                                    /*pendingTransactionQueueSize*/ 2u);
+    EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueSameLayerId_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueDifferentLayerId_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_removesSignaledFromTheQueue(createComposerStateVector(
+                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                      createComposerStateVector(
+                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepInTheQueueDifferentApplyToken_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceSignaled)));
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheQueueSameApplyToken_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+                                    /*updateApplyToken*/ false);
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheUnsignaledTheQueue_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+                                    /*updateApplyToken*/ false);
+    EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueue_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueueSameLayerId_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_RemovesFromTheQueueDifferentLayerId_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesSignaledFromTheQueue(createComposerStateVector(
+                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                      createComposerStateVector(
+                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_RemovesFromTheQueueDifferentApplyToken_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+                                                createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                        createComposerStateVector(
+                                                createComposerState(/*layerId*/ 2,
+                                                                    mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_RemovesUnsignaledFromTheQueueSameApplyToken_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+                                                createComposerState(/*layerId*/ 1,
+                                                                    mFenceUnsignaled)),
+                                        createComposerStateVector(
+                                                createComposerState(/*layerId*/ 2, mFenceSignaled)),
+                                        /*updateApplyToken*/ false);
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesUnsignaledFromTheQueue_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+                                                createComposerState(/*layerId*/ 1,
+                                                                    mFenceUnsignaled)),
+                                        createComposerStateVector(
+                                                createComposerState(/*layerId*/ 2,
+                                                                    mFenceUnsignaled)));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index a749ece..deeb785 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -46,6 +46,7 @@
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
         setupScheduler();
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+        mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     }
 
     ~TransactionFrameTracerTest() {
@@ -56,7 +57,7 @@
 
     sp<BufferStateLayer> createBufferStateLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
                                LayerMetadata());
         return new BufferStateLayer(args);
     }
@@ -92,21 +93,17 @@
     }
 
     TestableSurfaceFlinger mFlinger;
-    renderengine::mock::RenderEngine mRenderEngine;
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
 
     FenceToFenceTimeMap fenceFactory;
-    client_cache_t mClientCache;
 
     void BLASTTransactionSendsFrameTracerEvents() {
         sp<BufferStateLayer> layer = createBufferStateLayer();
 
         sp<Fence> fence(new Fence());
-        const auto buffer = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
+        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         int32_t layerId = layer->getSequence();
-        uint64_t bufferId = buffer->getBuffer()->getId();
+        uint64_t bufferId = buffer->getId();
         uint64_t frameNumber = 5;
         nsecs_t dequeueTime = 10;
         nsecs_t postTime = 20;
@@ -117,9 +114,14 @@
         EXPECT_CALL(*mFlinger.getFrameTracer(),
                     traceTimestamp(layerId, bufferId, frameNumber, postTime,
                                    FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
-        layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
-                         frameNumber, dequeueTime, FrameTimelineInfo{},
-                         nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint*/);
+        BufferData bufferData;
+        bufferData.buffer = buffer;
+        bufferData.acquireFence = fence;
+        bufferData.frameNumber = frameNumber;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, postTime, /*desiredPresentTime*/ 30, false, dequeueTime,
+                         FrameTimelineInfo{});
 
         commitTransaction(layer.get());
         bool computeVisisbleRegions;
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
new file mode 100644
index 0000000..6e00748
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <limits> // std::numeric_limits
+
+#include <gui/SurfaceComposerClient.h>
+
+#include "Tracing/TransactionProtoParser.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+TEST(TransactionProtoParserTest, parse) {
+    const sp<IBinder> layerHandle = new BBinder();
+    const sp<IBinder> displayHandle = new BBinder();
+    TransactionState t1;
+    t1.originPid = 1;
+    t1.originUid = 2;
+    t1.frameTimelineInfo.vsyncId = 3;
+    t1.frameTimelineInfo.inputEventId = 4;
+    t1.postTime = 5;
+
+    layer_state_t layer;
+    layer.layerId = 6;
+    layer.what = std::numeric_limits<uint64_t>::max();
+    layer.x = 7;
+    layer.matrix.dsdx = 15;
+
+    size_t layerCount = 2;
+    t1.states.reserve(layerCount);
+    for (uint32_t i = 0; i < layerCount; i++) {
+        ComposerState s;
+        if (i == 1) {
+            layer.parentSurfaceControlForChild =
+                    new SurfaceControl(SurfaceComposerClient::getDefault(), layerHandle, nullptr,
+                                       42);
+        }
+        s.state = layer;
+        t1.states.add(s);
+    }
+
+    size_t displayCount = 2;
+    t1.displays.reserve(displayCount);
+    for (uint32_t i = 0; i < displayCount; i++) {
+        DisplayState display;
+        display.what = std::numeric_limits<uint32_t>::max();
+        if (i == 0) {
+            display.token = displayHandle;
+        } else {
+            display.token = nullptr;
+        }
+        display.width = 85;
+        t1.displays.add(display);
+    }
+
+    TransactionProtoParser::LayerHandleToIdFn getLayerIdFn = [&](const sp<IBinder>& handle) {
+        return (handle == layerHandle) ? 42 : -1;
+    };
+    TransactionProtoParser::DisplayHandleToIdFn getDisplayIdFn = [&](const sp<IBinder>& handle) {
+        return (handle == displayHandle) ? 43 : -1;
+    };
+    TransactionProtoParser::LayerIdToHandleFn getLayerHandleFn = [&](int32_t id) {
+        return (id == 42) ? layerHandle : nullptr;
+    };
+    TransactionProtoParser::DisplayIdToHandleFn getDisplayHandleFn = [&](int32_t id) {
+        return (id == 43) ? displayHandle : nullptr;
+    };
+
+    proto::TransactionState proto =
+            TransactionProtoParser::toProto(t1, getLayerIdFn, getDisplayIdFn);
+    TransactionState t2 =
+            TransactionProtoParser::fromProto(proto, getLayerHandleFn, getDisplayHandleFn);
+
+    ASSERT_EQ(t1.originPid, t2.originPid);
+    ASSERT_EQ(t1.originUid, t2.originUid);
+    ASSERT_EQ(t1.frameTimelineInfo.vsyncId, t2.frameTimelineInfo.vsyncId);
+    ASSERT_EQ(t1.frameTimelineInfo.inputEventId, t2.frameTimelineInfo.inputEventId);
+    ASSERT_EQ(t1.postTime, t2.postTime);
+    ASSERT_EQ(t1.states.size(), t2.states.size());
+    ASSERT_EQ(t1.states[0].state.x, t2.states[0].state.x);
+    ASSERT_EQ(t1.states[0].state.matrix.dsdx, t2.states[0].state.matrix.dsdx);
+    ASSERT_EQ(t1.states[1].state.parentSurfaceControlForChild->getHandle(),
+              t2.states[1].state.parentSurfaceControlForChild->getHandle());
+
+    ASSERT_EQ(t1.displays.size(), t2.displays.size());
+    ASSERT_EQ(t1.displays[1].width, t2.displays[1].width);
+    ASSERT_EQ(t1.displays[0].token, t2.displays[0].token);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 2a7921f..704340d 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -46,6 +46,7 @@
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
         setupScheduler();
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+        mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     }
 
     ~TransactionSurfaceFrameTest() {
@@ -56,7 +57,7 @@
 
     sp<BufferStateLayer> createBufferStateLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
                                LayerMetadata());
         return new BufferStateLayer(args);
     }
@@ -92,10 +93,9 @@
     }
 
     TestableSurfaceFlinger mFlinger;
-    renderengine::mock::RenderEngine mRenderEngine;
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
 
     FenceToFenceTimeMap fenceFactory;
-    client_cache_t mClientCache;
 
     void PresentedSurfaceFrameForBufferlessTransaction() {
         sp<BufferStateLayer> layer = createBufferStateLayer();
@@ -114,13 +114,15 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        BufferData bufferData;
+        bufferData.buffer = buffer;
+        bufferData.acquireFence = fence;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         acquireFence->signalForTest(12);
 
         commitTransaction(layer.get());
@@ -143,27 +145,30 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        const auto buffer1 = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        BufferData bufferData;
+        bufferData.buffer = buffer1;
+        bufferData.acquireFence = fence1;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        const auto buffer2 = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
+        const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         nsecs_t start = systemTime();
-        layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        bufferData.buffer = buffer2;
+        bufferData.acquireFence = fence2;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         nsecs_t end = systemTime();
         acquireFence2->signalForTest(12);
 
@@ -198,13 +203,15 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        BufferData bufferData;
+        bufferData.buffer = buffer;
+        bufferData.acquireFence = fence;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         acquireFence->signalForTest(12);
 
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -227,13 +234,15 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        BufferData bufferData;
+        bufferData.buffer = buffer;
+        bufferData.acquireFence = fence;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
 
@@ -260,13 +269,15 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        BufferData bufferData;
+        bufferData.buffer = buffer;
+        bufferData.acquireFence = fence;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 3, /*inputEventId*/ 0});
         EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -299,25 +310,28 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        const auto buffer1 = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        BufferData bufferData;
+        bufferData.buffer = buffer1;
+        bufferData.acquireFence = fence1;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        const auto buffer2 = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        bufferData.buffer = buffer2;
+        bufferData.acquireFence = fence2;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         acquireFence2->signalForTest(12);
 
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -330,7 +344,7 @@
         // Both the droppedSurfaceFrame and presentedSurfaceFrame should be in
         // pendingJankClassifications.
         EXPECT_EQ(2u, layer->mPendingJankClassifications.size());
-        presentedSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+        presentedSurfaceFrame->onPresent(20, JankType::None, 90_Hz,
                                          /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
         layer->releasePendingBuffer(25);
 
@@ -342,27 +356,30 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        const auto buffer1 = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
-        layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        BufferData bufferData;
+        bufferData.buffer = buffer1;
+        bufferData.acquireFence = fence1;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        const auto buffer2 = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
+        const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         auto dropStartTime1 = systemTime();
-        layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
-                         nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint */);
+        bufferData.buffer = buffer2;
+        bufferData.acquireFence = fence2;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0});
         auto dropEndTime1 = systemTime();
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -370,14 +387,15 @@
 
         sp<Fence> fence3(new Fence());
         auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
-        const auto buffer3 = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                 1, 0),
-                                               mRenderEngine, false);
+        const auto buffer3 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         auto dropStartTime2 = systemTime();
-        layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
-                         nullptr /* releaseBufferEndpoint */);
+        bufferData.buffer = buffer3;
+        bufferData.acquireFence = fence3;
+        bufferData.frameNumber = 1;
+        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                         {/*vsyncId*/ 2, /*inputEventId*/ 0});
         auto dropEndTime2 = systemTime();
         acquireFence3->signalForTest(12);
 
@@ -413,16 +431,16 @@
         uint32_t surfaceFramesPendingClassification = 0;
         std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
         for (int i = 0; i < 10; i += 2) {
-            sp<Fence> fence1(new Fence());
-            const auto buffer1 = std::make_shared<
-                    renderengine::ExternalTexture>(new GraphicBuffer(1, 1,
-                                                                     HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                                                     0),
-                                                   mRenderEngine, false);
-            layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                             {/*vsyncId*/ 1, /*inputEventId*/ 0},
-                             nullptr /* releaseBufferCallback */,
-                             nullptr /* releaseBufferEndpoint */);
+            sp<Fence> fence(new Fence());
+            const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+            BufferData bufferData;
+            bufferData.buffer = buffer;
+            bufferData.acquireFence = fence;
+            bufferData.frameNumber = 1;
+            bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+            bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+            layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+                             {/*vsyncId*/ 1, /*inputEventId*/ 0});
             layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
                                                                   /*inputEventId*/ 0},
                                                                  10);
@@ -444,10 +462,10 @@
         // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
         // Since we don't have access to DisplayFrame here, trigger an onPresent directly.
         for (auto& surfaceFrame : bufferlessSurfaceFrames) {
-            surfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+            surfaceFrame->onPresent(20, JankType::None, 90_Hz,
                                     /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
         }
-        presentedBufferSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+        presentedBufferSurfaceFrame->onPresent(20, JankType::None, 90_Hz,
                                                /*displayDeadlineDelta*/ 0,
                                                /*displayPresentDelta*/ 0);
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
new file mode 100644
index 0000000..43b09fd
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+
+#include "Tracing/RingBuffer.h"
+#include "Tracing/TransactionTracing.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+class TransactionTracingTest : public testing::Test {
+protected:
+    static constexpr size_t SMALL_BUFFER_SIZE = 1024;
+    std::unique_ptr<android::TransactionTracing> mTracing;
+    void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); }
+
+    void TearDown() override {
+        mTracing->disable();
+        mTracing.reset();
+    }
+
+    auto getCommittedTransactions() {
+        std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock);
+        return mTracing->mCommittedTransactions;
+    }
+
+    auto getQueuedTransactions() {
+        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+        return mTracing->mQueuedTransactions;
+    }
+
+    auto getUsedBufferSize() {
+        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+        return mTracing->mBuffer->used();
+    }
+
+    auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); }
+
+    auto bufferFront() {
+        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+        proto::TransactionTraceEntry entry;
+        entry.ParseFromString(mTracing->mBuffer->front());
+        return entry;
+    }
+
+    bool threadIsJoinable() {
+        std::scoped_lock lock(mTracing->mMainThreadLock);
+        return mTracing->mThread.joinable();
+    }
+
+    proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); }
+
+    auto getCreatedLayers() {
+        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+        return mTracing->mCreatedLayers;
+    }
+
+    auto getStartingStates() {
+        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+        return mTracing->mStartingStates;
+    }
+
+    void queueAndCommitTransaction(int64_t vsyncId) {
+        TransactionState transaction;
+        transaction.id = static_cast<uint64_t>(vsyncId * 3);
+        transaction.originUid = 1;
+        transaction.originPid = 2;
+        mTracing->addQueuedTransaction(transaction);
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back(transaction);
+        mTracing->addCommittedTransactions(transactions, vsyncId);
+        flush(vsyncId);
+    }
+
+    // Test that we clean up the tracing thread and free any memory allocated.
+    void verifyDisabledTracingState() {
+        EXPECT_FALSE(mTracing->isEnabled());
+        EXPECT_FALSE(threadIsJoinable());
+        EXPECT_EQ(getCommittedTransactions().size(), 0u);
+        EXPECT_EQ(getQueuedTransactions().size(), 0u);
+        EXPECT_EQ(getUsedBufferSize(), 0u);
+        EXPECT_EQ(getStartingStates().size(), 0u);
+    }
+
+    void verifyEntry(const proto::TransactionTraceEntry& actualProto,
+                     const std::vector<TransactionState> expectedTransactions,
+                     int64_t expectedVsyncId) {
+        EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
+        EXPECT_EQ(actualProto.transactions().size(),
+                  static_cast<int32_t>(expectedTransactions.size()));
+        for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
+            EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
+                      expectedTransactions[i].originPid);
+        }
+    }
+};
+
+TEST_F(TransactionTracingTest, enable) {
+    EXPECT_FALSE(mTracing->isEnabled());
+    mTracing->enable();
+    EXPECT_TRUE(mTracing->isEnabled());
+    mTracing->disable();
+    verifyDisabledTracingState();
+}
+
+TEST_F(TransactionTracingTest, addTransactions) {
+    mTracing->enable();
+    std::vector<TransactionState> transactions;
+    transactions.reserve(100);
+    for (uint64_t i = 0; i < 100; i++) {
+        TransactionState transaction;
+        transaction.id = i;
+        transaction.originPid = static_cast<int32_t>(i);
+        transactions.emplace_back(transaction);
+        mTracing->addQueuedTransaction(transaction);
+    }
+
+    // Split incoming transactions into two and commit them in reverse order to test out of order
+    // commits.
+    std::vector<TransactionState> firstTransactionSet =
+            std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+    int64_t firstTransactionSetVsyncId = 42;
+    mTracing->addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+
+    int64_t secondTransactionSetVsyncId = 43;
+    std::vector<TransactionState> secondTransactionSet =
+            std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
+    mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+    flush(secondTransactionSetVsyncId);
+
+    proto::TransactionTraceFile proto = writeToProto();
+    EXPECT_EQ(proto.entry().size(), 3);
+    // skip starting entry
+    verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId);
+    verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId);
+
+    mTracing->disable();
+    verifyDisabledTracingState();
+}
+
+class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
+protected:
+    void SetUp() override {
+        TransactionTracingTest::SetUp();
+        mTracing->enable();
+        // add layers
+        mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+        const sp<IBinder> fakeLayerHandle = new BBinder();
+        mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
+                               123 /* flags */, -1 /* parentId */);
+        const sp<IBinder> fakeChildLayerHandle = new BBinder();
+        mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
+                               456 /* flags */, mParentLayerId);
+
+        // add some layer transaction
+        {
+            TransactionState transaction;
+            transaction.id = 50;
+            ComposerState layerState;
+            layerState.state.surface = fakeLayerHandle;
+            layerState.state.what = layer_state_t::eLayerChanged;
+            layerState.state.z = 42;
+            transaction.states.add(layerState);
+            ComposerState childState;
+            childState.state.surface = fakeChildLayerHandle;
+            childState.state.what = layer_state_t::eLayerChanged;
+            childState.state.z = 43;
+            transaction.states.add(childState);
+            mTracing->addQueuedTransaction(transaction);
+
+            std::vector<TransactionState> transactions;
+            transactions.emplace_back(transaction);
+            VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
+            mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+            flush(VSYNC_ID_FIRST_LAYER_CHANGE);
+        }
+
+        // add transactions that modify the layer state further so we can test that layer state
+        // gets merged
+        {
+            TransactionState transaction;
+            transaction.id = 51;
+            ComposerState layerState;
+            layerState.state.surface = fakeLayerHandle;
+            layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
+            layerState.state.z = 41;
+            layerState.state.x = 22;
+            transaction.states.add(layerState);
+            mTracing->addQueuedTransaction(transaction);
+
+            std::vector<TransactionState> transactions;
+            transactions.emplace_back(transaction);
+            VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
+            mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+            flush(VSYNC_ID_SECOND_LAYER_CHANGE);
+        }
+
+        // remove child layer
+        mTracing->onLayerRemoved(2);
+        VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId;
+        queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED);
+
+        // remove layer
+        mTracing->onLayerRemoved(1);
+        queueAndCommitTransaction(++mVsyncId);
+    }
+
+    void TearDown() override {
+        mTracing->disable();
+        verifyDisabledTracingState();
+        TransactionTracingTest::TearDown();
+    }
+
+    int mParentLayerId = 1;
+    int mChildLayerId = 2;
+    int64_t mVsyncId = 0;
+    int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
+    int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
+    int64_t VSYNC_ID_CHILD_LAYER_REMOVED;
+};
+
+TEST_F(TransactionTracingLayerHandlingTest, addStartingState) {
+    // add transactions until we drop the transaction with the first layer change
+    while (bufferFront().vsync_id() <= VSYNC_ID_FIRST_LAYER_CHANGE) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify we can still retrieve the layer change from the first entry containing starting
+    // states.
+    EXPECT_GT(proto.entry().size(), 0);
+    EXPECT_GT(proto.entry(0).transactions().size(), 0);
+    EXPECT_GT(proto.entry(0).added_layers().size(), 0);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).layer_id(), mChildLayerId);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) {
+    // add transactions until we drop the transaction with the second layer change
+    while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify starting states are updated correctly
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) {
+    // add transactions until we drop the transaction which removes the child layer
+    while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify the child layer has been removed from the trace
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) {
+    // add transactions until we drop the transaction with the second layer change
+    while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify we have two starting states
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+
+    // Continue adding transactions until child layer is removed
+    while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto = writeToProto();
+    // verify we still have the parent layer state
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
+protected:
+    void SetUp() override {
+        TransactionTracingTest::SetUp();
+        mTracing->enable();
+        // add layers
+        mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+        const sp<IBinder> fakeLayerHandle = new BBinder();
+        mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
+                               123 /* flags */, -1 /* parentId */);
+        const sp<IBinder> fakeMirrorLayerHandle = new BBinder();
+        mTracing->onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
+                                     mLayerId);
+
+        // add some layer transaction
+        {
+            TransactionState transaction;
+            transaction.id = 50;
+            ComposerState layerState;
+            layerState.state.surface = fakeLayerHandle;
+            layerState.state.what = layer_state_t::eLayerChanged;
+            layerState.state.z = 42;
+            transaction.states.add(layerState);
+            ComposerState mirrorState;
+            mirrorState.state.surface = fakeMirrorLayerHandle;
+            mirrorState.state.what = layer_state_t::eLayerChanged;
+            mirrorState.state.z = 43;
+            transaction.states.add(mirrorState);
+            mTracing->addQueuedTransaction(transaction);
+
+            std::vector<TransactionState> transactions;
+            transactions.emplace_back(transaction);
+            mTracing->addCommittedTransactions(transactions, ++mVsyncId);
+            flush(mVsyncId);
+        }
+    }
+
+    void TearDown() override {
+        mTracing->disable();
+        verifyDisabledTracingState();
+        TransactionTracingTest::TearDown();
+    }
+
+    int mLayerId = 5;
+    int mMirrorLayerId = 55;
+    int64_t mVsyncId = 0;
+    int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
+    int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
+    int64_t VSYNC_ID_CHILD_LAYER_REMOVED;
+};
+
+TEST_F(TransactionTracingMirrorLayerTest, canAddMirrorLayers) {
+    proto::TransactionTraceFile proto = writeToProto();
+    // We don't have any starting states since no layer was removed from.
+    EXPECT_EQ(proto.entry().size(), 2);
+    EXPECT_EQ(proto.entry(0).transactions().size(), 0);
+    EXPECT_EQ(proto.entry(0).added_layers().size(), 0);
+
+    // Verify the mirror layer was added
+    EXPECT_EQ(proto.entry(1).transactions().size(), 1);
+    EXPECT_EQ(proto.entry(1).added_layers().size(), 2);
+    EXPECT_EQ(proto.entry(1).added_layers(1).layer_id(), mMirrorLayerId);
+    EXPECT_EQ(proto.entry(1).transactions(0).layer_changes().size(), 2);
+    EXPECT_EQ(proto.entry(1).transactions(0).layer_changes(1).z(), 43);
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index e4f7469..15fea9c 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -27,7 +27,6 @@
 #include "TunnelModeEnabledReporter.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
 
 namespace android {
 
@@ -69,12 +68,12 @@
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
-    sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
-            new TestableTunnelModeEnabledListener();
-    sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
-            new TunnelModeEnabledReporter();
 
-    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+    sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
+            sp<TestableTunnelModeEnabledListener>::make();
+
+    sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
+            sp<TunnelModeEnabledReporter>::make();
 };
 
 TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() {
@@ -82,7 +81,6 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    mFlinger.mutableEventQueue().reset(mMessageQueue);
     setupScheduler();
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
     mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter;
@@ -100,8 +98,7 @@
 sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer(
         LayerMetadata metadata = {}) {
     sp<Client> client;
-    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
-                           LAYER_FLAGS, metadata);
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
     return new BufferStateLayer(args);
 }
 
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
index 41a4d30..21ee071 100644
--- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -44,7 +44,7 @@
 class WorkDurationTest : public testing::Test {
 protected:
     WorkDurationTest()
-          : mWorkDuration(Fps(60.0f), 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+          : mWorkDuration(60_Hz, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
                           21'000'000, 1234) {}
 
     ~WorkDurationTest() = default;
@@ -56,9 +56,9 @@
  * Test cases
  */
 TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) {
-    mWorkDuration.setRefreshRateFps(Fps(60.0f));
+    mWorkDuration.setRefreshRateFps(60_Hz);
     auto currentOffsets = mWorkDuration.getCurrentConfigs();
-    auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(60.0f));
+    auto offsets = mWorkDuration.getConfigsForRefreshRate(60_Hz);
 
     EXPECT_EQ(currentOffsets, offsets);
     EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
@@ -81,9 +81,9 @@
 }
 
 TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) {
-    mWorkDuration.setRefreshRateFps(Fps(90.0f));
+    mWorkDuration.setRefreshRateFps(90_Hz);
     auto currentOffsets = mWorkDuration.getCurrentConfigs();
-    auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(90.0f));
+    auto offsets = mWorkDuration.getConfigsForRefreshRate(90_Hz);
 
     EXPECT_EQ(currentOffsets, offsets);
     EXPECT_EQ(offsets.late.sfOffset, 611'111);
@@ -106,7 +106,7 @@
 }
 
 TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) {
-    TestableWorkDuration phaseOffsetsWithDefaultValues(Fps(60.0f), -1, -1, -1, -1, -1, -1, 0);
+    TestableWorkDuration phaseOffsetsWithDefaultValues(60_Hz, -1, -1, -1, -1, -1, -1, 0);
 
     auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) {
         EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
@@ -138,12 +138,12 @@
         validateOffsets(offsets, std::chrono::nanoseconds(refreshRate.getPeriodNsecs()));
     };
 
-    testForRefreshRate(Fps(90.0f));
-    testForRefreshRate(Fps(60.0f));
+    testForRefreshRate(90_Hz);
+    testForRefreshRate(60_Hz);
 }
 
 TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) {
-    auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(14.7f));
+    auto offsets = mWorkDuration.getConfigsForRefreshRate(14.7_Hz);
 
     EXPECT_EQ(offsets.late.sfOffset, 57'527'208);
     EXPECT_EQ(offsets.late.appOffset, 37'027'208);
@@ -181,13 +181,12 @@
                          std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
                          std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
                          nsecs_t thresholdForNextVsync, nsecs_t hwcMinWorkDuration)
-          : impl::PhaseOffsets(Fps(60.0f), vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
-                               earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
-                               earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
-                               highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
-                               highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
-                               highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync,
-                               hwcMinWorkDuration) {}
+          : impl::PhaseOffsets(60_Hz, vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs, earlySfOffsetNs,
+                               earlyGpuSfOffsetNs, earlyAppOffsetNs, earlyGpuAppOffsetNs,
+                               highFpsVsyncPhaseOffsetNs, highFpsSfVSyncPhaseOffsetNs,
+                               highFpsEarlySfOffsetNs, highFpsEarlyGpuSfOffsetNs,
+                               highFpsEarlyAppOffsetNs, highFpsEarlyGpuAppOffsetNs,
+                               thresholdForNextVsync, hwcMinWorkDuration) {}
 };
 
 class PhaseOffsetsTest : public testing::Test {
@@ -201,7 +200,7 @@
 };
 
 TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) {
-    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(14.7f));
+    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(14.7_Hz);
 
     EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
     EXPECT_EQ(offsets.late.appOffset, 2'000'000);
@@ -223,7 +222,7 @@
 }
 
 TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) {
-    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(60.0f));
+    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(60_Hz);
 
     EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
     EXPECT_EQ(offsets.late.appOffset, 2'000'000);
@@ -245,7 +244,7 @@
 }
 
 TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) {
-    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(90.0f));
+    auto offsets = mPhaseOffsets.getConfigsForRefreshRate(90_Hz);
 
     EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
     EXPECT_EQ(offsets.late.appOffset, 2'000'000);
@@ -269,7 +268,7 @@
 TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) {
     TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {},         2'000'000,
                                       1'000'000, {},        {}, {}, {}, 10'000'000, 1234};
-    auto offsets = phaseOffsets.getConfigsForRefreshRate(Fps(60.0f));
+    auto offsets = phaseOffsets.getConfigsForRefreshRate(60_Hz);
 
     EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
     EXPECT_EQ(offsets.late.appOffset, 1'000'000);
@@ -293,7 +292,7 @@
 TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) {
     TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {},         2'000'000,
                                       1'000'000, {},        {}, {}, {}, 10'000'000, 1234};
-    auto offsets = phaseOffsets.getConfigsForRefreshRate(Fps(90.0f));
+    auto offsets = phaseOffsets.getConfigsForRefreshRate(90_Hz);
 
     EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
     EXPECT_EQ(offsets.late.appOffset, 2'000'000);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 7de1872..20d41e6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -18,6 +18,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#undef LOG_TAG
 #define LOG_TAG "MockComposer"
 #include "mock/DisplayHardware/MockComposer.h"
 
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 1ba3c0f..ecdadf7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -48,6 +48,7 @@
     Composer();
     ~Composer() override;
 
+    MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
     MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>());
     MOCK_METHOD0(dumpDebugInfo, std::string());
     MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&));
@@ -61,7 +62,8 @@
     MOCK_METHOD2(destroyLayer, Error(Display, Layer));
     MOCK_METHOD2(getActiveConfig, Error(Display, Config*));
     MOCK_METHOD3(getChangedCompositionTypes,
-                 Error(Display, std::vector<Layer>*, std::vector<IComposerClient::Composition>*));
+                 Error(Display, std::vector<Layer>*,
+                       std::vector<aidl::android::hardware::graphics::composer3::Composition>*));
     MOCK_METHOD2(getColorModes, Error(Display, std::vector<ColorMode>*));
     MOCK_METHOD4(getDisplayAttribute,
                  Error(Display, Config config, IComposerClient::Attribute, int32_t*));
@@ -82,20 +84,23 @@
                  Error(Display, uint32_t, const sp<GraphicBuffer>&, int, Dataspace,
                        const std::vector<IComposerClient::Rect>&));
     MOCK_METHOD3(setColorMode, Error(Display, ColorMode, RenderIntent));
-    MOCK_METHOD3(setColorTransform, Error(Display, const float*, ColorTransform));
+    MOCK_METHOD2(setColorTransform, Error(Display, const float*));
     MOCK_METHOD3(setOutputBuffer, Error(Display, const native_handle_t*, int));
     MOCK_METHOD2(setPowerMode, Error(Display, IComposerClient::PowerMode));
     MOCK_METHOD2(setVsyncEnabled, Error(Display, IComposerClient::Vsync));
     MOCK_METHOD1(setClientTargetSlotCount, Error(Display));
-    MOCK_METHOD3(validateDisplay, Error(Display, uint32_t*, uint32_t*));
-    MOCK_METHOD5(presentOrValidateDisplay, Error(Display, uint32_t*, uint32_t*, int*, uint32_t*));
+    MOCK_METHOD4(validateDisplay, Error(Display, nsecs_t, uint32_t*, uint32_t*));
+    MOCK_METHOD6(presentOrValidateDisplay,
+                 Error(Display, nsecs_t, uint32_t*, uint32_t*, int*, uint32_t*));
     MOCK_METHOD4(setCursorPosition, Error(Display, Layer, int32_t, int32_t));
     MOCK_METHOD5(setLayerBuffer, Error(Display, Layer, uint32_t, const sp<GraphicBuffer>&, int));
     MOCK_METHOD3(setLayerSurfaceDamage,
                  Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
     MOCK_METHOD3(setLayerBlendMode, Error(Display, Layer, IComposerClient::BlendMode));
-    MOCK_METHOD3(setLayerColor, Error(Display, Layer, const IComposerClient::Color&));
-    MOCK_METHOD3(setLayerCompositionType, Error(Display, Layer, IComposerClient::Composition));
+    MOCK_METHOD3(setLayerColor,
+                 Error(Display, Layer, const aidl::android::hardware::graphics::composer3::Color&));
+    MOCK_METHOD3(setLayerCompositionType,
+                 Error(Display, Layer, aidl::android::hardware::graphics::composer3::Composition));
     MOCK_METHOD3(setLayerDataspace, Error(Display, Layer, Dataspace));
     MOCK_METHOD3(setLayerPerFrameMetadata,
                  Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadata>&));
@@ -116,9 +121,11 @@
                  Error(Display, uint64_t, uint64_t, DisplayedFrameStats*));
     MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
                  Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
-    MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
-    MOCK_METHOD0(isVsyncPeriodSwitchSupported, bool());
-    MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
+    MOCK_METHOD3(setDisplayBrightness, Error(Display, float, const DisplayBrightnessOptions&));
+    MOCK_METHOD2(
+            getDisplayCapabilities,
+            Error(Display,
+                  std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*));
     MOCK_METHOD2(getDisplayConnectionType,
                  V2_4::Error(Display, IComposerClient::DisplayConnectionType*));
     MOCK_METHOD3(getSupportedDisplayVsyncPeriods,
@@ -136,7 +143,11 @@
                              const std::vector<uint8_t>&));
     MOCK_METHOD1(getLayerGenericMetadataKeys,
                  V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
-    MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
+    MOCK_METHOD3(getClientTargetProperty,
+                 Error(Display, IComposerClient::ClientTargetProperty*, float*));
+    MOCK_METHOD3(setLayerWhitePointNits, Error(Display, Layer, float));
+    MOCK_METHOD3(setLayerBlockingRegion,
+                 Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index c3919d9..7ac0c78 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -30,7 +30,8 @@
     MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override));
     MOCK_METHOD(bool, isConnected, (), (const, override));
     MOCK_METHOD(void, setConnected, (bool), (override));
-    MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (),
+    MOCK_METHOD(bool, hasCapability,
+                (aidl::android::hardware::graphics::composer3::DisplayCapability),
                 (const, override));
     MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
     MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));
@@ -39,7 +40,9 @@
     MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (),
                 (override));
     MOCK_METHOD(hal::Error, getChangedCompositionTypes,
-                ((std::unordered_map<Layer *, hal::Composition> *)), (override));
+                ((std::unordered_map<Layer *,
+                                     aidl::android::hardware::graphics::composer3::Composition> *)),
+                (override));
     MOCK_METHOD(hal::Error, getColorModes, (std::vector<hal::ColorMode> *), (const, override));
     MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (), (const, override));
     MOCK_METHOD(hal::Error, getRenderIntents, (hal::ColorMode, std::vector<hal::RenderIntent> *),
@@ -67,17 +70,18 @@
                  const android::sp<android::Fence> &, hal::Dataspace),
                 (override));
     MOCK_METHOD(hal::Error, setColorMode, (hal::ColorMode, hal::RenderIntent), (override));
-    MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &, hal::ColorTransform),
-                (override));
+    MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
     MOCK_METHOD(hal::Error, setOutputBuffer,
                 (const android::sp<android::GraphicBuffer> &, const android::sp<android::Fence> &),
                 (override));
     MOCK_METHOD(hal::Error, setPowerMode, (hal::PowerMode), (override));
     MOCK_METHOD(hal::Error, setVsyncEnabled, (hal::Vsync), (override));
-    MOCK_METHOD(hal::Error, validate, (uint32_t *, uint32_t *), (override));
+    MOCK_METHOD(hal::Error, validate, (nsecs_t, uint32_t *, uint32_t *), (override));
     MOCK_METHOD(hal::Error, presentOrValidate,
-                (uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *), (override));
-    MOCK_METHOD(std::future<hal::Error>, setDisplayBrightness, (float), (override));
+                (nsecs_t, uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *),
+                (override));
+    MOCK_METHOD(std::future<hal::Error>, setDisplayBrightness,
+                (float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));
     MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
                 (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
                  hal::VsyncPeriodChangeTimeline *),
@@ -86,7 +90,8 @@
     MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *),
                 (const, override));
     MOCK_METHOD(hal::Error, setContentType, (hal::ContentType), (override));
-    MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *), (override));
+    MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *, float *),
+                (override));
 };
 
 class Layer : public HWC2::Layer {
@@ -102,8 +107,10 @@
                 (override));
     MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
     MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
-    MOCK_METHOD(hal::Error, setColor, (hal::Color), (override));
-    MOCK_METHOD(hal::Error, setCompositionType, (hal::Composition), (override));
+    MOCK_METHOD(hal::Error, setColor, (aidl::android::hardware::graphics::composer3::Color),
+                (override));
+    MOCK_METHOD(hal::Error, setCompositionType,
+                (aidl::android::hardware::graphics::composer3::Composition), (override));
     MOCK_METHOD(hal::Error, setDataspace, (android::ui::Dataspace), (override));
     MOCK_METHOD(hal::Error, setPerFrameMetadata, (const int32_t, const android::HdrMetadata &),
                 (override));
@@ -117,6 +124,8 @@
     MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
     MOCK_METHOD(hal::Error, setLayerGenericMetadata,
                 (const std::string &, bool, const std::vector<uint8_t> &), (override));
+    MOCK_METHOD(hal::Error, setWhitePointNits, (float whitePointNits), (override));
+    MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override));
 };
 
 } // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 159bdf1..c598cbc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -27,11 +27,20 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
-    MOCK_METHOD0(init, void());
-    MOCK_METHOD0(onBootFinished, void());
-    MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
-    MOCK_METHOD0(isUsingExpensiveRendering, bool());
-    MOCK_METHOD0(notifyDisplayUpdateImminent, void());
+    MOCK_METHOD(void, init, (), (override));
+    MOCK_METHOD(void, onBootFinished, (), (override));
+    MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
+                (override));
+    MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+    MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(bool, usePowerHintSession, (), (override));
+    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
+    MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
+                (override));
+    MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
+    MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index ba2e4db..0840a2f 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -25,7 +25,7 @@
 class MockLayer : public Layer {
 public:
     MockLayer(SurfaceFlinger* flinger, std::string name)
-          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
     explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
 
     MOCK_CONST_METHOD0(getType, const char*());
@@ -34,6 +34,7 @@
     MOCK_METHOD0(createClone, sp<Layer>());
     MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
     MOCK_CONST_METHOD0(getOwnerUid, uid_t());
+    MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
deleted file mode 100644
index 5fb06fd..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mock/MockMessageQueue.h"
-
-namespace android::mock {
-
-MessageQueue::MessageQueue() {
-    ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
-        // Execute task to prevent broken promise exception on destruction.
-        handler->handleMessage(Message());
-    });
-}
-
-MessageQueue::~MessageQueue() = default;
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
deleted file mode 100644
index 0e7b320..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "FrameTimeline.h"
-#include "Scheduler/EventThread.h"
-#include "Scheduler/MessageQueue.h"
-
-namespace android::mock {
-
-class MessageQueue : public android::MessageQueue {
-public:
-    MessageQueue();
-    ~MessageQueue() override;
-
-    MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>));
-    MOCK_METHOD0(waitMessage, void());
-    MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
-    MOCK_METHOD0(invalidate, void());
-    MOCK_METHOD0(refresh, void());
-    MOCK_METHOD3(initVsync,
-                 void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
-                      std::chrono::nanoseconds));
-    MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
-    MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>());
-};
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index ab19886..849e308 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -20,25 +20,22 @@
 
 #include "Scheduler/Scheduler.h"
 
-namespace android::mock {
+namespace android::scheduler::mock {
 
 struct SchedulerCallback final : ISchedulerCallback {
-    MOCK_METHOD1(setVsyncEnabled, void(bool));
-    MOCK_METHOD2(changeRefreshRate,
-                 void(const scheduler::RefreshRateConfigs::RefreshRate&,
-                      scheduler::RefreshRateConfigEvent));
-    MOCK_METHOD0(repaintEverythingForHWC, void());
-    MOCK_METHOD1(kernelTimerChanged, void(bool));
-    MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
+    MOCK_METHOD(void, scheduleComposite, (FrameHint), (override));
+    MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
+    MOCK_METHOD(void, changeRefreshRate, (const RefreshRate&, DisplayModeEvent), (override));
+    MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
+    MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
+    void scheduleComposite(FrameHint) override {}
     void setVsyncEnabled(bool) override {}
-    void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
-                           scheduler::RefreshRateConfigEvent) override {}
-    void repaintEverythingForHWC() override {}
+    void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() {}
 };
 
-} // namespace android::mock
+} // namespace android::scheduler::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 5aebd2f..0a69b56 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -41,19 +41,21 @@
     MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD6(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t, int32_t));
+    MOCK_METHOD(void, setPostTime,
+                (int32_t, uint64_t, const std::string&, uid_t, nsecs_t, GameMode), (override));
     MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
     MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
     MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD7(setPresentTime,
-                 void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote,
-                      int32_t));
-    MOCK_METHOD7(setPresentFence,
-                 void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
-                      SetFrameRateVote, int32_t));
+    MOCK_METHOD(void, setPresentTime,
+                (int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote, GameMode),
+                (override));
+    MOCK_METHOD(void, setPresentFence,
+                (int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+                 SetFrameRateVote, GameMode),
+                (override));
     MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 94d9966..314f681 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -27,7 +27,7 @@
     VsyncController();
     ~VsyncController() override;
 
-    MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override));
     MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
     MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
     MOCK_METHOD1(setIgnorePresentFences, void(bool));
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index a375808..63ecaec 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -402,12 +402,21 @@
         auto primitiveIdx = static_cast<size_t>(primitive);
         if (primitiveIdx >= durations.size()) {
             // Safety check, should not happen if enum_range is correct.
+            ALOGE("Supported primitive %zu is outside range [0,%zu), skipping load duration",
+                  primitiveIdx, durations.size());
             continue;
         }
         int32_t duration = 0;
-        auto status = getHal()->getPrimitiveDuration(primitive, &duration);
-        if (!status.isOk()) {
-            return HalResult<std::vector<milliseconds>>::failed(status.toString8().c_str());
+        auto result = getHal()->getPrimitiveDuration(primitive, &duration);
+        auto halResult = HalResult<int32_t>::fromStatus(result, duration);
+        if (halResult.isUnsupported()) {
+            // Should not happen, supported primitives should always support requesting duration.
+            ALOGE("Supported primitive %zu returned unsupported for getPrimitiveDuration",
+                  primitiveIdx);
+        }
+        if (halResult.isFailed()) {
+            // Fail entire request if one request has failed.
+            return HalResult<std::vector<milliseconds>>::failed(result.toString8().c_str());
         }
         durations[primitiveIdx] = milliseconds(duration);
     }
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index 6bf6581..0df0bfa 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -45,7 +45,7 @@
 template <typename T>
 HalResult<T> ManagerHalController::processHalResult(HalResult<T> result, const char* functionName) {
     if (result.isFailed()) {
-        ALOGE("%s failed: %s", functionName, result.errorMessage());
+        ALOGE("VibratorManager HAL %s failed: %s", functionName, result.errorMessage());
         std::lock_guard<std::mutex> lock(mConnectedHalMutex);
         mConnectedHal->tryReconnect();
     }
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 6c31e2b..6b73d17 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -103,7 +103,7 @@
 
         for (int i = 0; i < MAX_RETRIES; i++) {
             T result = halFn(hal.get());
-            if (result.checkAndLogFailure(functionName)) {
+            if (result.isFailedLogged(functionName)) {
                 tryReconnect();
             } else {
                 return result;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 68d6647..d2cc9ad 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -69,9 +69,9 @@
     bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
     bool isUnsupported() const { return mUnsupported; }
     const char* errorMessage() const { return mErrorMessage.c_str(); }
-    bool checkAndLogFailure(const char* functionName) const {
+    bool isFailedLogged(const char* functionNameForLogging) const {
         if (isFailed()) {
-            ALOGE("%s failed: %s", functionName, errorMessage());
+            ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage());
             return true;
         }
         return false;
@@ -107,9 +107,9 @@
     bool isFailed() const { return !mUnsupported && mFailed; }
     bool isUnsupported() const { return mUnsupported; }
     const char* errorMessage() const { return mErrorMessage.c_str(); }
-    bool checkAndLogFailure(const char* functionName) const {
+    bool isFailedLogged(const char* functionNameForLogging) const {
         if (isFailed()) {
-            ALOGE("%s failed: %s", functionName, errorMessage());
+            ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage());
             return true;
         }
         return false;
@@ -192,21 +192,21 @@
     const HalResult<float> qFactor;
     const HalResult<std::vector<float>> maxAmplitudes;
 
-    bool checkAndLogFailure(const char*) const {
-        return capabilities.checkAndLogFailure("getCapabilities") ||
-                supportedEffects.checkAndLogFailure("getSupportedEffects") ||
-                supportedBraking.checkAndLogFailure("getSupportedBraking") ||
-                supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") ||
-                primitiveDurations.checkAndLogFailure("getPrimitiveDuration") ||
-                primitiveDelayMax.checkAndLogFailure("getPrimitiveDelayMax") ||
-                pwlePrimitiveDurationMax.checkAndLogFailure("getPwlePrimitiveDurationMax") ||
-                compositionSizeMax.checkAndLogFailure("getCompositionSizeMax") ||
-                pwleSizeMax.checkAndLogFailure("getPwleSizeMax") ||
-                minFrequency.checkAndLogFailure("getMinFrequency") ||
-                resonantFrequency.checkAndLogFailure("getResonantFrequency") ||
-                frequencyResolution.checkAndLogFailure("getFrequencyResolution") ||
-                qFactor.checkAndLogFailure("getQFactor") ||
-                maxAmplitudes.checkAndLogFailure("getMaxAmplitudes");
+    bool isFailedLogged(const char*) const {
+        return capabilities.isFailedLogged("getCapabilities") ||
+                supportedEffects.isFailedLogged("getSupportedEffects") ||
+                supportedBraking.isFailedLogged("getSupportedBraking") ||
+                supportedPrimitives.isFailedLogged("getSupportedPrimitives") ||
+                primitiveDurations.isFailedLogged("getPrimitiveDuration") ||
+                primitiveDelayMax.isFailedLogged("getPrimitiveDelayMax") ||
+                pwlePrimitiveDurationMax.isFailedLogged("getPwlePrimitiveDurationMax") ||
+                compositionSizeMax.isFailedLogged("getCompositionSizeMax") ||
+                pwleSizeMax.isFailedLogged("getPwleSizeMax") ||
+                minFrequency.isFailedLogged("getMinFrequency") ||
+                resonantFrequency.isFailedLogged("getResonantFrequency") ||
+                frequencyResolution.isFailedLogged("getFrequencyResolution") ||
+                qFactor.isFailedLogged("getQFactor") ||
+                maxAmplitudes.isFailedLogged("getMaxAmplitudes");
     }
 };
 
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 26052fb..33401d2 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -560,6 +560,7 @@
     }
 
     static const char* const known_non_device_names[] = {
+        "vkAcquireDrmDisplayEXT",
         "vkCreateAndroidSurfaceKHR",
         "vkCreateDebugReportCallbackEXT",
         "vkCreateDebugUtilsMessengerEXT",
@@ -581,6 +582,7 @@
         "vkEnumeratePhysicalDevices",
         "vkGetDisplayModeProperties2KHR",
         "vkGetDisplayPlaneCapabilities2KHR",
+        "vkGetDrmDisplayEXT",
         "vkGetInstanceProcAddr",
         "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT",
         "vkGetPhysicalDeviceDisplayPlaneProperties2KHR",
@@ -624,6 +626,8 @@
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
         "vkGetPhysicalDeviceSurfaceSupportKHR",
         "vkGetPhysicalDeviceToolPropertiesEXT",
+        "vkGetPhysicalDeviceVideoCapabilitiesKHR",
+        "vkGetPhysicalDeviceVideoFormatPropertiesKHR",
         "vkSubmitDebugUtilsMessageEXT",
     };
     // clang-format on
diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h
index e5b1587..416c0bc 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -78,8 +78,7 @@
         VkDebugReportCallbackEXT driver_handle;
     };
 
-    // TODO(b/143295577): use std::shared_mutex when available in libc++
-    mutable std::shared_timed_mutex rwmutex_;
+    mutable std::shared_mutex rwmutex_;
     Node head_;
 };
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 6191063..4a6b4f1 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -23,6 +23,8 @@
 #include <sync/sync.h>
 #include <system/window.h>
 #include <ui/BufferQueueDefs.h>
+#include <ui/DebugUtils.h>
+#include <ui/PixelFormat.h>
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
@@ -462,21 +464,24 @@
     *count = num_copied;
 }
 
-android_pixel_format GetNativePixelFormat(VkFormat format) {
-    android_pixel_format native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+android::PixelFormat GetNativePixelFormat(VkFormat format) {
+    android::PixelFormat native_format = android::PIXEL_FORMAT_RGBA_8888;
     switch (format) {
         case VK_FORMAT_R8G8B8A8_UNORM:
         case VK_FORMAT_R8G8B8A8_SRGB:
-            native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+            native_format = android::PIXEL_FORMAT_RGBA_8888;
             break;
         case VK_FORMAT_R5G6B5_UNORM_PACK16:
-            native_format = HAL_PIXEL_FORMAT_RGB_565;
+            native_format = android::PIXEL_FORMAT_RGB_565;
             break;
         case VK_FORMAT_R16G16B16A16_SFLOAT:
-            native_format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            native_format = android::PIXEL_FORMAT_RGBA_FP16;
             break;
         case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
-            native_format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            native_format = android::PIXEL_FORMAT_RGBA_1010102;
+            break;
+        case VK_FORMAT_R8_UNORM:
+            native_format = android::PIXEL_FORMAT_R_8;
             break;
         default:
             ALOGV("unsupported swapchain format %d", format);
@@ -696,10 +701,10 @@
     if (err) {
         return VK_ERROR_SURFACE_LOST_KHR;
     }
-    ALOGV("wide_color_support is: %d", wide_color_support);
-    wide_color_support =
-        wide_color_support &&
+    bool swapchain_ext =
         instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
+    ALOGV("wide_color_support is: %d", wide_color_support);
+    wide_color_support = wide_color_support && swapchain_ext;
 
     AHardwareBuffer_Desc desc = {};
     desc.width = 1;
@@ -714,6 +719,11 @@
         {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
         {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
 
+    if (swapchain_ext) {
+        all_formats.emplace_back(VkSurfaceFormatKHR{
+            VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
+    }
+
     if (wide_color_support) {
         all_formats.emplace_back(VkSurfaceFormatKHR{
             VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
@@ -753,6 +763,13 @@
         }
     }
 
+    desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM;
+    if (AHardwareBuffer_isSupported(&desc)) {
+        all_formats.emplace_back(
+            VkSurfaceFormatKHR{VK_FORMAT_R8_UNORM,
+                               VK_COLOR_SPACE_PASS_THROUGH_EXT});
+    }
+
     VkResult result = VK_SUCCESS;
     if (formats) {
         uint32_t transfer_count = all_formats.size();
@@ -1029,7 +1046,7 @@
     if (!allocator)
         allocator = &GetData(device).allocator;
 
-    android_pixel_format native_pixel_format =
+    android::PixelFormat native_pixel_format =
         GetNativePixelFormat(create_info->imageFormat);
     android_dataspace native_dataspace =
         GetNativeDataspace(create_info->imageColorSpace);
@@ -1126,8 +1143,8 @@
 
     err = native_window_set_buffers_format(window, native_pixel_format);
     if (err != android::OK) {
-        ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
-              native_pixel_format, strerror(-err), err);
+        ALOGE("native_window_set_buffers_format(%s) failed: %s (%d)",
+              decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = native_window_set_buffers_data_space(window, native_dataspace);
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 72fd4fb..4176509 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -33,6 +33,7 @@
     'VK_EXT_metal_surface',
     'VK_FUCHSIA_imagepipe_surface',
     'VK_GGP_stream_descriptor_surface',
+    'VK_HUAWEI_subpass_shading',
     'VK_KHR_display',
     'VK_KHR_display_swapchain',
     'VK_KHR_external_fence_win32',
@@ -47,11 +48,13 @@
     'VK_MVK_ios_surface',
     'VK_MVK_macos_surface',
     'VK_NN_vi_surface',
+    'VK_NV_acquire_winrt_display',
     'VK_NV_cooperative_matrix',
     'VK_NV_coverage_reduction_mode',
     'VK_NV_external_memory_win32',
     'VK_NV_win32_keyed_mutex',
     'VK_NVX_image_view_handle',
+    'VK_QNX_screen_surface',
 ]
 
 # Extensions having functions exported by the loader.